'use strict' #--------------------------------------------------------------- # # 画面 # class Screen constructor: (_s, id) -> @bgs = _s['BGS'] @objs = _s['OBJS'] canvas = document.getElementById(id) @context = canvas.getContext('2d') r = canvas.getBoundingClientRect() @x0 = r.left; @y0 = r.top; @width = r.width; @height = r.height draw: -> @context.fillStyle = '#000000' @context.fillRect(0, 0, @width, @height) @bgs[2].draw() # BG2: 背景 for type, objs of @objs for obj in objs obj.draw() @bgs[1].draw() # BG1: 地形 @bgs[0].draw() # BG0: 情報 #--------------------------------------------------------------- # # イメージプレーン # class Plane @image_load = [] @image_src: (pats) -> for n in [0...pats.length] image = new Image; image.src = pats[n] @image_load.push(pats[n] = image) @image_complete: -> for image in @image_load return(false) unless(image.complete) return(true) constructor: (_s) -> @_s = _s @context = _s['SCREEN'].context draw: -> #--------------------------------------------------------------- # # オブジェクト # class Obj extends Plane constructor: (_s, x, y) -> super(_s) @objs = _s['OBJS'] @_px8 = (@pos_x = x) << 8; @_py8 = (@pos_y = y) << 8 # ドット x256 位置 @active = true # check_hit: -> check_hit_obj: (t) -> cx = t.pos_x + t.ent_x - @pos_x >>> 0 return(false) unless(cx <= @ent_x + t.ent_x) # X No Hit cy = t.pos_y + t.ent_y - @pos_y >>> 0 return(false) unless(cy <= @ent_y + t.ent_y) # Y No Hit return(true) # hit: -> destroy: -> @active = false ##nclude 'rotnscl.bean' #--------------------------------------------------------------- # 64 # ベクトル計算 | # dx, dy -> -x, -y | x, -y # | # v -> 0_______|_______ # 255 | 128 # | class Vec # -x, y | x, y # | # 192 @vxyss = []; n = 256; for t in [0..5] # n: 方向の分解能, v: 方向, t: 速度倍率 vxys = []; for v in [0..(n >> 3)] rd = 2 * Math.PI * v / n vx = -Math.floor(Math.cos(rd) * 256 * t) vy = -Math.floor(Math.sin(rd) * 256 * t) vxys[ v] = [ vx, vy] vxys[ 64 - v] = [ vy, vx] vxys[ 64 + v] = [-vy, vx] vxys[128 - v] = [-vx, vy] vxys[128 + v] = [-vx, -vy] vxys[192 - v] = [-vy, -vx] vxys[192 + v] = [ vy, -vx] vxys[256 - v] = [ vx, -vy] @vxyss.push(vxys) @v2vxy: (v, t = 3) -> # 方向 v -> 速度成分 [vx, vy] if(it = @vxyss[t]) then it[v] else [@vxyss[1][v][0] * t, @vxyss[1][v][1] * t] @dxy2v: (dx, dy) -> # 目標との差分 dx, dy -> 方向 v (分解能は 64) v = 0; s = 8 if(dy > 0) then ([dx, dy] = [-dx, -dy]; v += 32) # dy (対象 - 自分) が正、対象は下にいる if(dx > 0) then ([dx, dy] = [ dy, -dx]; v += 16) # dx (対象 - 自分) が正、対象は右にいる if(dx > dy) then ([dx, dy] = [ dy, dx]; v += 15; s = -s) # -dx > -dy が正、対象は 45 度以上にいる for i in [1..3] # 45 度を 8(2^3) 分割してサーチ dx >>= 1; s >>= 1; if(dx > dy) then (v += s; dy -= dx) return(v << 2) @dxy2vxy: (dx, dy, t = 3) -> # 目標との差分 dx, dy -> 速度成分 [vx, vy] @v2vxy(@dxy2v(dx, dy), t) #--------------------------------------------------------------- # # Wasm モジュール # class Wasm @objs = {} @obj_src: (objs) -> for n in [0...objs.length] unless(@objs[objs[n][0]]) @objs[objs[n][0]] = await WebAssembly.instantiateStreaming(fetch(objs[n][0]), objs[n][1]) @func: (func) -> @objs[func].instance.exports @check: -> for func, objs of @objs console.log('wasm: ', func, objs) #--------------------------------------------------------------- # # 回転(拡大縮小)可能オブジェクト(mixin用) # Rotnscl = (base) -> class extends base # w:512 x h:512 x 4 = 1,048,576 = 16 PAGE # (cos:4 + sin:4) x 256 = 2,048 # 16 PAGE(dest) + 16 PAGE(src) + 2,048 @r_memory = new WebAssembly.Memory({ initial: 33 }) # 1 PAGE = 64 KB importObjects = { js: { mem: @r_memory }, console: { log: (arg) => console.log(arg) }, } Wasm.obj_src([ ['rotnscl.wasm', importObjects], ]) rotnscl_init: -> #--------------------------------------------------------------- # 64 # ベクトル計算 | # dx, dy -> -x, -y | x, -y # | # v -> 0_______|_______ # 255 | 128 # | # -x, y | x, y # | # 192 vxys = new Int32Array(@constructor.r_memory.buffer, 2 * 4 * 512 * 512, 2 * 257) n = 256; for v in [0..(n >> 3)] # n: 方向の分解能, v: 方向 rd = 2 * Math.PI * v / n vx = -Math.floor(Math.cos(rd) * 65536) vy = -Math.floor(Math.sin(rd) * 65536) vxys[it = v << 1] = vx; vxys[it + 1] = vy vxys[it = 64 - v << 1] = vy; vxys[it + 1] = vx vxys[it = 64 + v << 1] = -vy; vxys[it + 1] = vx vxys[it = 128 - v << 1] = -vx; vxys[it + 1] = vy vxys[it = 128 + v << 1] = -vx; vxys[it + 1] = -vy vxys[it = 192 - v << 1] = -vy; vxys[it + 1] = -vx vxys[it = 192 + v << 1] = vy; vxys[it + 1] = -vx vxys[it = 256 - v << 1] = vx; vxys[it + 1] = -vy # range over # 透過色を設定 vxys[512] = 0x00000000 # AA: 00 transparent <-> opaque FF # AABBGGRR @rotnscl_wasm = Wasm.func('rotnscl.wasm') rotnscl: (pats, n, v, t, w = null, h = null, dx = null, dy = null) -> @rotnscl_init() unless(@rotnscl_wasm) pat = pats[n] w ||= pat.width; dx ||= -(w >> 1) h ||= pat.height; dy ||= -(h >> 1) return(rpat) if(rpat = @constructor.rpats[rsym = "#{n}_#{t}_#{v}_#{w}_#{h}_#{dx}_#{dy}"]) unless(@constructor.pdats[psym = "in_work_memory"] == n) # ソースイメージをデータ化 src_canvas = document.createElement('canvas') src_canvas.width = pat.width src_canvas.height = pat.height src_context = src_canvas.getContext('2d') src_context.drawImage(pat, 0, 0) src_bytes = src_context.getImageData(0, 0, pat.width, pat.height).data src_longs = new BigUint64Array(src_bytes.buffer, 0, src_bytes.length >> 3) # コピーの高速化のために共用体化 work_bytes = new Uint8ClampedArray(@constructor.r_memory.buffer, 0, src_bytes.length) work_longs = new BigUint64Array(@constructor.r_memory.buffer, 4 * 512 * 512, src_longs.length) # コピーの高速化のために共用体化 # ソースイメージデータをワークメモリにコピー for p in [0...src_longs.length] work_longs[p] = src_longs[p] @constructor.pdats[psym] = n # # 回転画像生成の実行 # @rotnscl_wasm.rotnscl(pats, n, v, t, w = null, h = null, dx = null, dy = null) @rotnscl_wasm.rotnscl(256 - v, w) # TODO # 生成データをイメージ化 rotnscled_bytes = new Uint8ClampedArray(@constructor.r_memory.buffer, 0, 4 * w * h) rotnscled_image = new ImageData(rotnscled_bytes, w, h) # スプライト(=キャンバス要素)を生成 rot_canvas = document.createElement('canvas') rot_canvas.width = w rot_canvas.height = h rot_context = rot_canvas.getContext('2d') rot_context.putImageData(rotnscled_image, 0, 0) return(@constructor.rpats[rsym] = rot_canvas) cache_status: -> console.log([@constructor.name, Object.keys(@constructor.pdats).length, Object.keys(@constructor.rpats).length].toString()) if(tsc % 60 == 0) #--------------------------------------------------------------- # # オブジェクトサンプル # class Sample extends Rotnscl(Obj) Plane.image_src(@pats = [ # 'images/sample0.png' # 0: 48 x 48 # 'images/tetran1.png' # 0: 64 x 64 'images/tetran2.png' # 0: 128 x 128 # 'images/tetran3.png' # 0: 512 x 512 ]) @pdats = {}; @rpats = {} constructor: (_s, x, y) -> super(_s, x, y) @_vx8 = 0 << 8; @_vy8 = 0 << 7 draw: -> n = 0 # パターンナンバ v = tsc & 0xFF # 角度 t = 1 # 倍率 rpat = @rotnscl(@constructor.pats, n, v, t) @context.drawImage(rpat, @pos_x, @pos_y) d0: -> @_px8 += @_vx8; @_py8 += @_vy8 @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 class Sounds # dummy @sound_complete: -> true class BackGround extends Plane # dummy constructor: (_s) -> super(_s) draw: -> class Director # dummy constructor: (_s) -> @_s = _s @bgs = _s['BGS'] @objs = _s['OBJS'] initialize: -> @bgs.push(new BackGround(@_s)) @bgs.push(new BackGround(@_s)) @bgs.push(new BackGround(@_s)) @objs['SAMPLES'].push(new Sample(@_s, 100, 100)) @objs['SAMPLES'].push(new Sample(@_s, 160, 160)) d0: -> for type, objs of @objs # 各オブジェクトの移動, 破棄 n = 0; while(n < objs.length) objs[n].d0() unless(objs[n].active) then objs.splice(n, 1) else ++n #=============================================================================== # # メイン # console.log(['hello, world'].toString()) _s = BGS: [] OBJS: SAMPLES: [] #--------------------------------------------------------------- # # 各種初期設定 # prep = -> unless(Plane.image_complete() and Sounds.sound_complete()) setTimeout(prep, 100) else _s['SCREEN'] = new Screen(_s, 'canvas1') _s['DIRECTOR'] = new Director(_s) _s['DIRECTOR'].initialize() vsync() #--------------------------------------------------------------- # # 垂直帰線割込みのシミュレート # tsc = 0; ints = []; spf = (1000 / 60) # フレーム速度(1000/FPS) vsync = -> ints.push(start = (new Date).getTime()); next = start + spf ints.shift() while(ints[0] < start - 1000) _s['SCREEN'].draw() _s['DIRECTOR'].d0() finish = (new Date).getTime() if(++tsc % 60 == 0) console.log(['now: ', start, 'frames: ', ints.length, ' load: ', Math.floor((finish - start) * 100 / spf), '%'].toString()) setTimeout(vsync, if((it = next - finish) > 0) then it else 0) prep()