HSP Syntax Highlighter 1.3


    
    ; 疑似3D道路を描画する / Drawing pseudo 3D roads
    ; ビルボードスプライトの表示を実装

    #include "hspmath.as"

    #define BG0_FILE    "bg_bg0.png"
    #define BG1_FILE    "bg_bg1.png"
    #define SPR_FILE    "trees.png"
    
    #define SCR_W       640
    #define SCR_H       360
    ; #define SCR_W       1280
    ; #define SCR_H       720
    
    #define FRAMERATE   30
    
   ; セグメントの区切りを色分け. 0 or 1 or 2
    #define SEG_DELIMITER   0
    
    ; 同梱ファイル / bundled files
    #pack   BG0_FILE
    #pack   BG1_FILE
    #pack   SPR_FILE
    
    #packopt name "road04"      ; 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
    dispw = ginfo_winx
    disph = ginfo_winy
    cls 4

    ; 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 :

    ; セグメント作成用データ
    dim segm, 18, 4
    ; set_array 配列変数名, index, count, curve, pitch, billboard
    ; curve と pitch は100倍した値。
    ; HSPは int と double を配列内で混在できないらしいので…
    set_array segm,  0, 25,    0,    0, 0
    set_array segm,  1, 28,    0,    0, BB_BEAM
    set_array segm,  2, 20,    0,    0, 0
    set_array segm,  3, 20,    0,    0, BB_ARROWL
    set_array segm,  4, 10,   50,    0, BB_TREE
    set_array segm,  5, 60,  200,    0, BB_TREE
    set_array segm,  6, 10,   50,    0, BB_TREE
    set_array segm,  7, 50,    0,  100, BB_GRASS
    set_array segm,  8, 20,    0,    0, BB_ARROWR
    set_array segm,  9, 10, -100,    0, BB_GRASS
    set_array segm, 10, 30, -400,    0, BB_TREE
    set_array segm, 11, 10, -100,    0, 0
    set_array segm, 12, 20,    0, -100, BB_TREE
    set_array segm, 13, 50,  -50,    0, BB_GRASS
    set_array segm, 14, 20,    0,    0, BB_ARROWL
    set_array segm, 15, 25,  200,    0, 0
    set_array segm, 16, 20,    0,    0, 0
    set_array segm, 17, 30,    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)
        lp = segm(cnt, 0)
        curve = segm(cnt, 1)
        pitch = segm(cnt, 2)
        billboard = segm(cnt, 3)
        
        repeat lp
            seg(i, 0) = z
            seg(i, 1) = curve
            seg(i, 2) = pitch
            
            ; set sprite
            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 + 40
                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 {
                    spr_kind = 5 + (billboard - BB_ARROWR)
                    spr_x = road_x + 120
                    if billboard == 3 : spr_x *= -1
                    spr_scale = 100
                } else {
                    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 {
                    spr_kind = 8
                    spr_x = 0
                    spr_scale = 100
                } else {
                    spr_kind = 0
                    spr_x = 0
                    spr_scale = 100
                }
                swbreak
            swend
            
            seg(i, 3) = spr_kind
            seg(i, 4) = spr_x
            seg(i, 5) = spr_scale
            
            z += seg_length
            i++
        loop
    loop
    
    ; 画面に描画するセグメントの個数
    seg_distance = 96
    
    ; セグメントの画像上の位置等を記録するバッファ
    ddim seg_x0, seg_distance
    ddim seg_y0, seg_distance
    ddim seg_z0, seg_distance
    ddim seg_cx0, seg_distance
    ddim seg_x1, seg_distance
    ddim seg_y1, seg_distance
    ddim seg_z1, seg_distance
    ddim seg_cx1, seg_distance

    ; セグメントに付随するスプライト相当の情報
    dim seg_spr_kind, seg_distance
    ddim seg_spr_x, seg_distance
    ddim seg_spr_w, seg_distance
    ddim seg_spr_scale, seg_distance
    
    ; 画面までの距離 / distance to screen
    dist = 200.0
    
    camera_z = 0.0
    
    spd = 0.0
    spd_max = 16.0 * (60.0 / double(FRAMERATE))
    spda = spd_max * 0.00625

    xd = 0.0
    yd = 0.0
    zd = double(seg_length)
    
    bgx = 0.0

*mainloop

    #define KEY_ESC     128
    #define KEY_UP      2
    #define KEY_DOWN    8
    #define KEY_LEFT    1
    #define KEY_RIGHT   4
    
    ; ESC key to exit
    stick k, 255
    if k & KEY_ESC : goto *jobend
    
    ; 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
    }
    if k & KEY_LEFT : bg_x -= 16
    if k & KEY_RIGHT : bg_x += 16

    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
    
    ; カメラがセグメント内のどの位置に居るか割合を求める
    z0 = seg(seg_index, 0)
    z1 = z0 + seg_length
    ccz = camera_z \ seg_total_length
    camz = double(ccz - z0) / double(z1 - z0)

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

    ; 道路を描画するためのデータを計算
    i = 0
    seg_z_max = 0
    repeat seg_distance
        ; セグメントの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
        z1 = z0 + seg_length
        
        if z0 = 0 : z0 = 1      ; 0で除算しないように微調整
        
        ; 画面上で描画すべきy座標位置を取得
        y0 = int(road_y * dist / z0)
        y1 = int(road_y * dist / z1)
        
        ; 画面上で描画すべき道路の幅(片側の幅)を取得
        x0 = double(road_x) * double(dist) / double(z0)
        x1 = double(road_x) * double(dist) / double(z1)

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

        ; 描画用に記録
        seg_z0(i) = double(z0)
        seg_z1(i) = double(z1)
        seg_y0(i) = double(y0)
        seg_y1(i) = double(y1)
        seg_x0(i) = double(x0)
        seg_x1(i) = double(x1)
        seg_cx0(i) = double(cx0)
        seg_cx1(i) = double(cx1)

        ; スプライト相当の情報も記録
        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
    
    ; 背景(遠景)を描画 / draw BG
    if SEG_DELIMITER <= 1 {
        bgw = bg1w
        bgh = bg1h
        bgx = int(double(bg_x) * 0.6) \ bgw
        bgy = bgh / 2 - base_y
        bgid = bgimgid1
        gmode gmode_gdi
        gosub *draw_bg

        bgw = bg0w
        bgh = bg0h
        bgx = int(double(bg_x) * 0.8) \ bgw
        bgy = bgh / 2 - base_y
        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
        x0 = seg_x0(i)
        y0 = seg_y0(i)
        z0 = seg_z0(i)
        cx0 = seg_cx0(i)
        x1 = seg_x1(i)
        y1 = seg_y1(i)
        z1 = seg_z1(i)
        cx1 = seg_cx1(i)
        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 {
            h = abs(y1 - y0)
            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_sy > 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 + (dispw / 2) + center_x
                
                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
            ; 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

    ; draw text
    font "Arial", 18, 1
    color 1, 1, 1
    pos 8, 4
    mes "Press cursor key"
    pos 8, disph - 32
    mes "seg_index : " + seg_index

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

*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

*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 = double(disph) * 1.2
        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 = double(disph) * 0.4
        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 = double(disph) * 0.6
        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 = double(disph) * 3.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 / 3) : gosub *draw_sprite_gzoom
    }
    return

*draw_sprite_gzoom
    ; スプライト相当を拡大縮小して描画
    ; dst_x, dst_y : 描画先の左上座標
    ; dst_w, dst_h : 描画先のサイズ
    ; imgid : 元画像バッファID
    ; imgw, imgh : 元画像サイズ
    ; n : スプライトNo.
    
    ; テクスチャのソース座標を算出
    src_w = (imgw / 4)
    src_h = (imgh / 2)
    src_x = (n & 3) * src_w
    src_y = ((n >> 2) & 1) * src_h

    if dstx_x < dispw & (dst_x + dst_w) > 0 {
        ; gzoom は透明色を扱えないので小技が必要
        ; 一旦、仮バッファに拡大縮小描画
        gsel tmpid
        pos 0, 0
        gzoom dst_w, dst_h, imgid, src_x, src_y, src_w, src_h, 0

        ; 仮バッファから実スクリーンに gcopy でコピー
        ; gcopy なら RGB=(0,0,0) を透明色として扱える
        gsel 0
        gmode gmode_rgb0    ; RGB=(0,0,0)を透明色として扱う
        pos dst_x, dst_y
        gcopy tmpid, 0, 0, dst_w, dst_h
    }
    return

*jobend
    end
      

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