PICO-8でゲーム制作を学ぶ(3)

[合いの手] PICO-8を学び中。

を学んで、

効果音も作ってみた。

図: 効果音

ソースコードも入力。

図: ソースコード

[合いの手] ところでプレイ動画はどうやって撮影するんだっけ?

[話者] 無料版については、 https://www.lexaloffle.com/bbs/?tid=47278 に書いてある。

  • [Ctrl]+[8] で撮影開始。
  • [Ctrl]+[9] で撮影完了して、
  • 画面右上にアニメGIFが表示されるので、右クリック>保存。

[合いの手] 撮影できた。 これがプレイ動画。mp4じゃなくて、gifだから、音は出ないんだね。

図:

ソースコード、前回との違いは少ない。

x=64
y=64
sprite=1
dir=1
itvl_frame=8 -- interval =1/30sec
anim_frame=2
t=0
item_x=32
item_y=32
item_sprite=5
function input()
 local pressed=false
 if btn(0) then x-=1 dir=4 pressed=true end
 if btn(1) then x+=1 dir=3 pressed=true end
 if btn(2) then y-=1 dir=2 pressed=true end
 if btn(3) then y+=1 dir=1 pressed=true end
 if pressed then 
  sprite=dir+flr((t%(anim_frame*itvl_frame))/itvl_frame+1)*16
 else
  sprite=dir
 end
end
function distance(x1,y1,x2,y2) return sqrt((x1-x2)^2+(y1-y2)^2) end
function collide(x1,y1,x2,y2)
 if distance(x1,y1,x2,y2) < 4 then return true end
 return false
end
function warp_item() item_x=rnd(128) item_y=rnd(128) end
function _update()
 t+=1
 input()
 if collide(x,y,item_x,item_y) then warp_item() sfx(0) end
end
function _draw()
 rectfill(0,0,127,127,13)
 spr(item_sprite,item_x-4,item_y-4)
 spr(sprite,x-4,y-4)
end

[話者] distance関数(距離を計算する)と、collide関数(衝突しているか判定する)を使っているくらいだな。

[合いの手] ソースコード入りPNG画像ファイルに、スクショを埋め込むにはどうするの?

[話者] ゲームをプレイ中に Ctrl+7 を押してから、
SAVE 20240327-1.p8.png
みたいにPNG画像を保存すればいい。

[合いの手] こうか。
ソースコード入りPNGは以下だよ。

図: 20240327-1.p8.png

次は

プレイ内容

図: アイテム取得時間を競う

ソースコード

図: ソースコード

x=64 y=64
sprite=1
dir=1
itvl_frame=8 -- interval per frame 1/30sec
anim_frame=2
speed=2
items={}
time=0
t=0
item_sprite=5

function update_timer()
 if #items>0 then time+=1 end
end

function draw_timer()
 local centi=flr(time*10/3)%100;
 if centi<10 then centi="0"..centi end
 local sec=flr(time/30)
 local min=flr(sec/60)
 sec=sec%60
 if sec<10 then sec="0"..sec end
 local col=7
 if #items==0 then col=rnd(16) end
 print(min..":"..sec.."."..centi, 2, 120, col)
end

function input()
 local pressed=false
 if btn(0) then x-=speed dir=4 pressed=true end
 if btn(1) then x+=speed dir=3 pressed=true end
 if btn(2) then y-=speed dir=2 pressed=true end
 if btn(3) then y+=speed dir=1 pressed=true end
 if pressed then
  sprite=dir+flr((t%(anim_frame*itvl_frame))/itvl_frame+1)*16
 else
  sprite=dir
 end
end

function distance(x1,y1,x2,y2) return sqrt((x1-x2)^2+(y1-y2)^2) end

function collide(x1,y1,x2,y2)
 if distance(x1,y1,x2,y2) < 6 then return true end
 return false
end

function place_items()
 local i=1
 for y=0,120,8 do
  for x=0,120,8 do
   items[i]={x=x+4,y=y+4}
   i+=1
  end
 end
end

function draw_items()
 for item in all(items) do
  spr(item_sprite, item.x-4, item.y-4)
 end
end

--

function _init()
 place_items()
end

function _update()
 t+=1
 input()
 for item in all(items) do
  if collide(x,y,item.x,item.y) then del(items, item) sfx(0) end
 end
 update_timer()
end

function _draw()
 rectfill(0,0,127,127,13)
 draw_items()
 spr(sprite,x-4,y-4)
 draw_timer()
end

[話者] タイマー処理の部分は

タイマーの表示も組み込まれています。
1/30秒おきに1ずつ増えるカウンターを「分:秒.1/100秒」に分解する処理が行われています。

具体的には

  • flr()JavaScriptでいう Math.floor() で、小数点以下を削除する。
  • sec="0"..sec というのは 1 ではなく 01 にする処理だな。

配列変数は、Lua言語ではtableと呼ぶ。JavaScriptのObjectと同じ感じだね。 配列の添え数は、JavaScriptなら 0 から始まるけど、 Luaは1 から始まるのが特徴的。

[合いの手] time 変数と t 変数の違いはなんだろ?

[話者] どちらも1/30秒ごとに +1 されるけど、
time 変数は、青リンゴをすべて取得してしまうと、+1 されなくなるんだ。
t 変数は青リンゴと関係なく +1 される。

この t 変数、本当はもっとケアすべきなんだよね。

PICO-8説明書

  • PICO-8の数値は32767.99までしか扱えません。
    毎フレームカウンターに1を足すと、18分後にはオーバーフローを起こします!

と書いてあるように、32700 くらいを超えて、+1 すると、マイナス値の -32700 くらいになるので、
長時間プレイするゲームなら、
t = (t + 1) % 32700
とかの処理を入れて、適度に 0 に戻すほうがいいんだけどね。 このソースコードでは、やってない。

[合いの手] これは短時間しかプレイしないゲームなので、そこまでケアしてないってことか。

ゲームの画面をどう作って更新するのか、気になってたけど、PICO-8は、
1/30秒ごとに全部消して、
毎回1から描き直す、
重ね順で下のほうを先に描いて、後から描いたのは重ね順の上になる
という更新方法なんだね。

なんか毎回書き直すのは無駄な気もするけど、単純だし、これでも いいな。

ソースコードは以下。

図: 20240327-1.p8.png