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