; 疑似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