「TIC-80」のタイルマップ
「TIC-80」には標準でタイルマップ機能があります。マップエディタ(GUI)でタイル(BG)を選択してクリックやドラッグで配置するほかmset(x,y,id)
という命令でプログラムから配置することも可能です。
マップエディタでの配置はファミコンの「ロードランナー」や「ナッツ&ミルク(配置方法はこちらが近い)」みたいな感じです。
(若い人には伝わらないかも‥‥)えっと最近のゲームなら、そう、マリオメーカーみたいな感じです。
主な仕様は以下の通り
- 1つのセルに対し8x8ドットの背景タイル(id:0~255)を設定する
- ワールドマップは240x136セル (1920x1088 ピクセル)
タイルマップの表示は map 命令で行ないます。
map ([x=0, y=0], [w=30, h=17], [sx=0, sy=0], [colorkey=-1], [scale=1], [remap=nil])
- x,y は描画を開始する(左上の)ワールドマップ上のセル座標
- w,h は描画する範囲の横幅と高さ(マップのセル単位)
- sx,sy は(マップ左上の)描画座標(画面上のピクセル単位)
- colorkey は透明にしたい色番号(0~15,-1の場合透明なし)
- scale は表示倍率(1以上の整数?)
- remap は‥‥こおるばっく関数??(便利らしいのですがサンプルも見つけられず使い方が分かりませんでした)
タイルマップのループ
ワールドマップは上下左右がループしています。
マップエディタでWORLD MAP表示(タブキーか目玉アイコン(?)をクリック)
上の画像では表示範囲を表す赤枠は右下にしかありませんが、下の画像ではワールドマップの四隅のタイルも表示されています。
エディタ上だけでなく map 命令で表示させた場合も 240x136セル範囲外のものは反対側に回り込んで表示してくれます。
ただしこれはワールドマップでの仕様なので、それより小さな範囲でループさせたい場合は、map 命令を複数回実行(もしくはマップデータを周辺にコピー)する必要があります(多分)。
で、以前は、ワールドマップの(0,0)
に配置した32x32セル(固定)のエリアをループ表示させるために中心と上下左右斜め全てで合計9回 map 命令を呼んでいました。
96x96セル分描画してその内画面に表示されるのは全画面でも 32x17 セル分という。
その後、せめてもう少し減らそうということで常時5回に減らしたりしましたが
表示エリアの大きさ固定、全画面固定だったのでこの度、マップ表示部分を
書き直しました。
実装
function lmap(mapx,mapy,mw,mh,sx,sy,colkey,scale)
local mx,my=mapx or 0,mapy or 0
local lw,lh=mw or 30,mh or 17
local xx,yy=sx or 0,sy or 0
local ck,s=colkey or -1,scale or 1
return function(x,y,w,h,ox,oy,noloop)
local x,y=x%lw,y%lh
local ww,hh=w or 30,h or 17
local ox,oy=ox or 0,oy or 0
map(mx+x,my+y,min(ww,lw-x),min(hh,lh-y),xx+ox,yy+oy,ck,s)
if x+ww>lw and not noloop then
map(mx,my+y,x+ww-lw,min(hh,lh-y),xx+(lw-x)*s*8+ox,yy+oy,ck,s)end
if y+hh>lh and not noloop then
map(mx+x,my,min(ww,lw-x),y+hh-lh,xx+ox,yy+(lh-y)*s*8+oy,ck,s)end
if x+ww>lw and y+hh>lh and not noloop then
map(mx,my,x+ww-lw,y+hh-lh,xx+(lw-x)*s*8+ox,yy+(lh-y)*s*8+oy,ck,s)end end end
testinit=function()
x,y=0,0
lm=lmap(0,0,16,16,80,24)
end
testinit()
function TIC()
if btnp(0,1,30)then y=y-1 end
if btnp(1,1,30)then y=y+1 end
if btnp(2,1,30)then x=x-1 end
if btnp(3,1,30)then x=x+1 end
lm(x,y,8,8)
end
こんな感じかな?
説明のために図を書きましたが、最初からこれを書いてから実装した方が良かった気がします。
(数式部分はパッっと書けた訳ではなく、こうかな?どうかな?みたいな感じで
違ったら少し変更して‥とかしていたので)
間違いがありましたら、ご指摘お願いします。
使い方としては
lmap(mapx,mapy,mw,mh,sx,sy,colkey,scale)
- mapx,mapy (ワールドマップ座標)
- mw,mh (制限幅)
で制限(ワールドマップから切り出すイメージ)して、返り値の命令にさらに
命令(x,y,w,h)
で相対座標と表示範囲をパラメータとして与えて実行します。
実行例
以前に紹介したスプライトデータからテーブルに、テーブルから境界線付きマップデータに変換、
と合わせて実行するとこのような感じです。
TEIJIRO氏の「すわいん」を思い出しました(伝わる人がいたら嬉しい)
スプライトデータからマップデータを作成しているので、
スプライトをそのまま表示させると(右下)ミニマップになるのがポイント☆
最後に一応全リストを載せておきます。
min=math.min
-- common function from TIC-80 wiki
function sget(x,y) -- get spritesheet pixel
local addr=0x4000+(x//8+y//8*16)*32
return peek4(addr*2+x%8+y%8*8) end
-- my common function
function spr2tbl(idx,w,h,x_offset,y_offset)
local w,h=w or 8,h or 8
local ox,oy=x_offset or 0,y_offset or 0
local sprX,sprY=idx%16*8,idx//16*8
local tbl={}
for rown=1,h do
tbl[rown]={}
for coln=1,w do
tbl[rown][coln]=sget((sprX+ox+coln-1)%128,sprY+oy+rown-1)end end
return tbl end
function area2map(t,colsync,bgcol,ldcol,zerotid,tnum,x_offset,y_offset,dosync)
local csync=colsync or false
local ox,oy=x_offset or -1,y_offset or -1
local bgc,ldc=bgcol or 13,ldcol or 4
local ztid,tn=zerotid or 0,tnum or 4
for rown,row in ipairs(t) do
for coln,num in ipairs(row) do
local stid
if csync then
stid=num*tn else
stid=num==0 and bgc*tn or ldc*tn end
local dLeft=(coln==1 or num==t[rown][coln-1])and 0 or 1
local dUp=(rown==1 or num==t[rown-1][coln])and 0 or 2
mset(coln+ox,rown+oy,ztid+stid+dLeft+dUp)end end
if dosync then sync(4,0,true) end end
function lmap(mapx,mapy,mw,mh,sx,sy,colkey,scale)
local mx,my=mapx or 0,mapy or 0
local lw,lh=mw or 30,mh or 17
local xx,yy=sx or 0,sy or 0
local ck,s=colkey or -1,scale or 1
return function(x,y,w,h,ox,oy,noloop)
local x,y=x%lw,y%lh
local ww,hh=w or 30,h or 17
local ox,oy=ox or 0,oy or 0
map(mx+x,my+y,min(ww,lw-x),min(hh,lh-y),xx+ox,yy+oy,ck,s)
if x+ww>lw and not noloop then
map(mx,my+y,x+ww-lw,min(hh,lh-y),xx+(lw-x)*s*8+ox,yy+oy,ck,s)end
if y+hh>lh and not noloop then
map(mx+x,my,min(ww,lw-x),y+hh-lh,xx+ox,yy+(lh-y)*s*8+oy,ck,s)end
if x+ww>lw and y+hh>lh and not noloop then
map(mx,my,x+ww-lw,y+hh-lh,xx+(lw-x)*s*8+ox,yy+(lh-y)*s*8+oy,ck,s)end end end
testinit=function()
sidx=80
x,y=0,0
t=spr2tbl(sidx,16,16)
area2map(t,true)
lm=lmap(0,0,16,16,80,24)
end
testinit()
function TIC()
if btnp(0,1,30)then y=y-1 end
if btnp(1,1,30)then y=y+1 end
if btnp(2,1,30)then x=x-1 end
if btnp(3,1,30)then x=x+1 end
lm(x,y,8,8)
spr(sidx,200,100,-1,1,0,0,2,2)
end