#!ruby -Ku # -*- mode: ruby; coding: utf-8 -*- # Last updated: <2016/05/08 09:41:27 +0900> # # グリッド状態の座標群をスクロールさせる仕組みがちゃんと動くかテスト # ポリゴンが重なる問題を少しだけ回避した版 # しかしやっぱりおかしな状態が発生する… require "dxruby" # 道路生成用クラス class Road # 座標記録用クラス class Point attr_accessor :x, :y, :z def initialize(x, y, z) @x = x @y = y @z = z end def set_pos(x, y, z) @x = x @y = y @z = z end def set_pos_point(b) @x = b.x @y = b.y @z = b.z end # 座標を引く def minus(b) return Point.new(@x - b.x, @y - b.y, @z - b.z) end # 外積を求める def cross_product(b) x = (@y * b.z) - (@z * b.y) y = (@z * b.x) - (@x * b.z) z = (@x * b.y) - (@y * b.x) return Point.new(x, y, z) end # 正規化 def normalize l = Math.sqrt( (@x * @x) + (@y * @y) + (@z * @z)) return Point.new(@x / l, @y / l, @z / l) end def dump puts "(#{@x} , #{@y} , #{@z})" end end attr_accessor :idx def initialize @imgs = Image.loadTiles("tex.png", 1, 3) # 画像読み込み @Face = Struct.new("Face", :indexes, :visible, :tex) # ポリゴン記録用 @road_w = 80 # 道路の幅 @side_w = 32 # 道路脇の一片の幅 @dist = 32 # 道路の一片の長さ @side_num = 6 # 道路脇の片の数(片側分) @rownum = 16 # 道路片の数 @colnum = (@side_num + 1) * 2 # 横一列分の頂点数 @x, @z = 0, 0 # 道路生成の基準座標 @idx = 0 # インデックス値 @ang = 270 # 初期角度 @p = [] # 頂点群の情報 @road_lines = [] # 道路の線分情報 @faces = [] # ポリゴン群の情報 # 座標格納用配列を確保 @rownum.times do |i| @road_lines.push([Point.new(0, 0, 0), Point.new(0, 0, 0)]) @colnum.times { |j| @p.push(Point.new(0, 0, 0)) } end # 座標群を初期化 @rownum.times do |i| set_oneline(i, @ang, @dist, @road_w, @side_w) adjust_polygon_overlap(i) if i > 1 # @ang += rand(-20..20) @ang += 5 end # ポリゴンの頂点リストを設定 @rownum.times do |row| v = (row < (@rownum - 1))? true : false texk = row % 2 i0 = (row * @colnum) % @p.length i1 = ((row + 1) * @colnum) % @p.length (@colnum - 1).times do |i| tk = (i == @side_num)? texk : 2 lst = [ i1 + i, i1 + i + 1, i0 + i + 1, i0 + i, ] @faces.push(@Face.new(lst, v, tk)) end end end # 道路の横一列分の座標群を設定 # # @param idx [Integer] 更新場所のインデックス値 # @param ang [Number] 角度 # @param dist [Number] 道路一片の長さ # @param road_w [Number] 道路の横幅 # @param side_w [Number] 道路脇一片の横幅 def set_oneline(idx, ang, dist, road_w, side_w) # 線分の開始座標を記録 @road_lines[idx][0].x = @x @road_lines[idx][0].z = @z # 座標群を更新 l = @side_num + 1 i0 = idx * @colnum i1 = i0 + @colnum - 1 rad0 = deg2rad(ang - 90) rad1 = deg2rad(ang + 90) r = side_w * @side_num + (road_w / 2) l.times do |j| @p[i0 + j].x = @x + r * Math.cos(rad0) @p[i0 + j].z = @z + r * Math.sin(rad0) @p[i1 - j].x = @x + r * Math.cos(rad1) @p[i1 - j].z = @z + r * Math.sin(rad1) r -= side_w end # 次の基準座標を設定 rad = deg2rad(ang) @x += dist * Math.cos(rad) @z += dist * Math.sin(rad) # 線分の終了座標を記録 @road_lines[idx][1].x = @x @road_lines[idx][1].z = @z end # ポリゴンの重なりを解決する def adjust_polygon_overlap(idx) l = @side_num + 1 # 道路左脇のポリゴンをチェック i2 = idx * @colnum + @side_num i0 = ((i2 - @colnum * 2) + @p.length) % @p.length i1 = (i0 + @colnum) % @p.length a = @p[i1].minus(@p[i0]) b = @p[i2].minus(@p[i1]) c = a.cross_product(b) if c.y > 0 # 道路が左に向いてる i0 = idx * @colnum i1 = ((i0 - @colnum) + @p.length) % @p.length i2 = i1 + 1 l.times do |j| a = @p[i1].minus(@p[i0]) b = @p[i2].minus(@p[i1]) c = a.cross_product(b) # ひっくり返っていたら補正 if c.y < 0 @p[i0].set_pos_point(@p[i1]) end i0 += 1 i1 += 1 i2 += 1 end end # 道路右脇のポリゴンをチェック i2 = idx * @colnum + @side_num + 1 i0 = ((i2 - @colnum * 2) + @p.length) % @p.length i1 = (i0 + @colnum) % @p.length a = @p[i1].minus(@p[i0]) b = @p[i2].minus(@p[i1]) c = a.cross_product(b) if c.y < 0 # 道路が右に向いている i0 = ((idx + 1) * @colnum - 1) % @p.length i1 = idx * @colnum - 1 i2 = i1 - 1 l.times do |j| a = @p[i1].minus(@p[i2]) b = @p[i0].minus(@p[i1]) c = a.cross_product(b) if c.y < 0 @p[i0].set_pos_point(@p[i1]) end i0 -= 1 i1 -= 1 i2 -= 1 end end end # 道路を1マス進める def inc_index(add_ang) set_oneline(@idx, @ang, @dist, @road_w, @side_w) adjust_polygon_overlap(@idx) fnum = @colnum - 1 # 非表示ポリゴンの表示を有効にする f0 = ((@idx + @rownum - 1) % @rownum)* fnum fnum.times do |i| @faces[f0 + i].visible = true end # 一部のポリゴンを非表示にする f0 = @idx * fnum fnum.times do |i| @faces[f0 + i].visible = false end # インデックス値を更新 @idx = (@idx + 1) % @rownum # 角度を更新 # @ang += rand(-20..20) @ang += add_ang end # 度からラジアンに変換 # # @param deg [Number] 度 # @return [Number] ラジアン def deg2rad(deg) return deg * Math::PI / 180.0 end # ポリゴンを描画 # # @param bx [Number] 描画をずらす量 x # @param by [Number] 描画をずらす量 y def draw(bx, by) @faces.each do |f| next unless f.visible idxs = f.indexes x1, y1 = @p[idxs[0]].x + bx, @p[idxs[0]].z + by x2, y2 = @p[idxs[1]].x + bx, @p[idxs[1]].z + by x3, y3 = @p[idxs[2]].x + bx, @p[idxs[2]].z + by x4, y4 = @p[idxs[3]].x + bx, @p[idxs[3]].z + by Window.drawMorph(x1, y1, x2, y2, x3, y3, x4, y4, @imgs[f.tex]) end end # 道路の線分上の座標値を取得 # # @param idx [Integer] インデックス値 # @param t [Number] 割合。0.0 - 1.0 の値 # @param [Array] x,z座標を返す def get_roadlinepos(idx, t) idx = (idx + @rownum) % @rownum l0, l1 = @road_lines[idx] x = l0.x + (l1.x - l0.x) * t z = l0.z + (l1.z - l0.z) * t return x, z end end fnt = Font.new(16) road = Road.new t = 0 cnt = 0 Window.loop do break if Input.keyPush?(K_ESCAPE) # カーソルキーが押されたら道路の座標群を一部更新 # if Input.keyPush?(K_LEFT) or Input.keyPush?(K_RIGHT) # road.inc_index((Input.keyPush?(K_LEFT))? -10 : 10) # end t += (1.0 / 60) * 2 if t >= 1.0 t -= 1.0 ang = Math.sin(cnt * Math::PI / 180.0) * 15 road.inc_index(ang) cnt += 15 end bx, by = road.get_roadlinepos(road.idx + 2, t) road.draw(-bx + Window.width / 2, -by + Window.height / 2) Window.drawFont(4, 4, "Index : #{road.idx}", fnt) end