0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IchigoJamAdvent Calendar 2024

Day 13

【IchigoJam】ぬるぽガッゲーム / 大石泉すきゲーム

Posted at

IchigoJam で、「ぬるぽ」と出たら素早く「ガッ」するゲームを作ってみた。
その文字のデータを差し替えて、「大石泉」が出たら素早く「すき」するゲームも作ってみた。

※IchigoJamはjig.jpの登録商標です。

東雲フォント (BDF) を埋め込む

今回は、Public Domain となっているビットマップフォントの「東雲フォント」のうち、16×16ピクセルの shnmk16.bdf を使用した。

このファイルでは、文字の情報が以下のような形式で記述されている。

shnmk16.bdf (抜粋)
STARTCHAR 472d
ENCODING 18221
SWIDTH 960 0
DWIDTH 16 0
BBX 16 16 0 -2
BITMAP
0488
4488
2fff
1088
1088
2800
4bfe
0a22
1a22
1a22
2bfe
4a22
0a22
0a22
6bfe
1000
ENDCHAR

今回用いる shnmk16.bdf では、文字の大きさは16×16ピクセルで固定であり、BITMAP から ENDCHAR までの部分が文字の形を表しているようである。
また、STARTCHAR の右に書かれているのが文字を JIS で表したときの文字コードのようである。
この文字コードは、たとえばサクラエディタで文字コードを JIS に設定し、調べたい文字の左側にキャレットを置くことで確認できる。
文字の文字コードを確認したら、その文字コードで shnmk16.bdf 内を検索することで、その文字のフォントが得られる。

このプログラムを実行し、BITMAPENDCHAR の間のデータを入力として流し込む (貼り付ける) ことで、文字の形を確認できる。
なお、描画する位置のリセットは実装していないので、画面の下まで描画したあと更に描画したい場合は、一度実行を止めて再実行することでリセットするとよい。

IchigoJam BASIC
10 ' フォント ヒョウジ
20 CLS:Y=0:X=0
30 K=INKEY()
40 IF #30<=K AND K<=#39 P=K-#30:GOTO90
50 IF #41<=K AND K<=#46 P=K-#41+10:GOTO90
60 IF #61<=K AND K<=#66 P=K-#61+10:GOTO90
70 IF K=#0A Y=Y+1:X=0
80 GOTO 30
90 FOR I=0 TO 3
100 IF P&8 DRAW X,Y
110 P=P<<1:X=X+1
120 NEXT
130 LOCATE 0,Y/2+1
140 GOTO 30

OneFiveCrowd で実行する / IchigoJam web で実行する

例として、上に掲載した文字のデータを入力すると、以下のように文字の形を確認できる。

文字の形を確認する例

POKE で埋め込む

使う文字のデータを、以下の変換器で IchigoJam のキャラクターパターンの形式に変換する。

See the Pen 16x16 BDF converter by MikeCAT (@mike_cat) on CodePen.

コード
HTML
<p>in</p>
<textarea id="inarea" rows="18" cols="8"></textarea>
<p>out</p>
<textarea id="outarea" rows="6" cols="50" readonly></textarea>
JS
const inarea = document.getElementById("inarea");
const outarea = document.getElementById("outarea");

inarea.addEventListener("change", () => {
	const lines = inarea.value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
	let res = "";
	for (let i = 0; i <= lines.length - 8; i += 8) {
		for (let j = 0; j < 8; j++) {
			res += parseInt(lines[i + j].substring(0, 2), 16) + ",";
		}
		for (let j = 0; j < 8; j++) {
			res += parseInt(lines[i + j].substring(2, 4), 16) + ",";
		}
	}
	outarea.value = res;
});

そして、それを POKE 命令でキャラクターパターン領域に書き込む。
(ゲームとしての実装を想定し、問題カウントと得点を想定した出力もしている)

IchigoJam BASIC
10 ' ヌルポ ガッ
20 POKE#700,0,0,16,16,19,28,16,16,0,128,128,128,240,136,132,130,41,41,70,68,74,48,0,0,2,2,2,58,68,68,58,0,0,0,16,16,17,22,24,24,0,64,64,64,240,136,132,132,41,37,66,66,77,48,0,0,2,2,2,4,4,8,48,0
30 POKE#740,0,7,0,0,1,2,7,12,0,224,64,128,0,0,224,16,16,32,0,3,4,4,3,0,8,4,4,4,136,112,192,0,0,7,0,0,0,1,3,6,0,224,32,64,128,0,240,8,8,16,0,0,0,0,1,0,4,4,4,8,16,96,128,0
40 POKE#780,0,19,16,16,16,32,35,32,6,249,25,22,16,28,240,16,32,32,32,33,42,18,17,0,16,16,16,240,28,18,224,0,0,16,16,16,16,19,32,32,22,25,25,22,30,240,16,16,32,32,32,33,42,18,17,0
50 POKE#7B8,16,16,16,240,28,18,224,0,0,1,1,1,63,1,1,1,0,2,9,5,252,8,8,8,1,2,2,4,8,16,32,0,8,8,8,8,8,16,96,0,0,0,0,0,0,0,2,17,0,0,0,0,0,0,0,16,9,8,0,0,0,0,3,0,16,16,32,32,64,128,0,0
60 VIDEO3:CLS
70 LOCATE0,0:?"11/15":LOCATE11,0:?"12345"
80 LOCATE4,4:?CHR$(#E0,#E1,#E8,#E9,#F0,#F1):LOCATE4,5:?CHR$(#E2,#E3,#EA,#EB,#F2,#F3)
90 LOCATE10,7:?CHR$(#F8,#F9,#FC,#FD):LOCATE10,8:?CHR$(#FA,#FB,#FE,#FF)

このプログラムは 954 バイトを消費し、1024 バイト中残りは 70 バイトである。
ゲームを実装するには、より短い表現でないといけなそうだ。

配列で埋め込む

POKE 命令は1バイトずつ書き込むため、1バイトずつ数字を書く必要があり、数値の分割や区切り文字の影響で長くなりやすい。
配列を用いることで、2バイトずつ書き込むことができ、長さを減らすことができる。
配列に書き込んだあと、COPY 命令を使用してキャラクターパターン領域にデータを転送する。

以下のプログラムにより、前章のキャラクターパターン形式を配列形式に変換できる。

IchigoJam BASIC
10 COPY #800,#700,128
20 FOR I=0 TO 63
30 IF [I]<-9999 ?"#";HEX$([I]); ELSE ?[I];
40 IF I=31 OR I=63 ?"" ELSE ?",";
50 NEXT

このプログラムは、キャラクターパターンデータの前半の変換を行う。10行目の #700#780 にすることで、後半の変換を行うことができる。

変換した結果を埋め込むと、以下のコードになった。

IchigoJam BASIC
10 ' ヌルポ ガッ
20 LET[0],0,4112,7187,4112,#8000,#8080,#88F0,#8284,10537,17478,12362,0,514,14850,17476,58,0,4112,5649,6168,16384,16448,#88F0,#8484,9513,16962,12365,0,514,1026,2052,48
30 LET[32],1792,0,513,3079,-8192,#8040,0,4320,8208,768,1028,3,1032,1028,28808,192,1792,0,256,1539,-8192,16416,128,2288,4104,0,0,1,1028,2052,24592,128:COPY#700,#800,128
40 LET[0],4864,4112,8208,8227,-1786,5657,7184,4336,8224,8480,4650,17,4112,-4080,4636,224,4096,4112,4880,8224,6422,5657,-4066,4112,8224,8480,4650,17,4112,-4080,4636,224
50 LET[32],256,257,319,257,512,1289,2300,2056,513,1026,4104,32,2056,2056,4104,96,0,0,0,4354,0,0,0,4096,2057,0,0,3,4112,8224,#8040,0:COPY#780,#800,128
60 VIDEO3:CLS
70 LOCATE0,0:?"11/15":LOCATE11,0:?"12345"
80 LOCATE4,4:?CHR$(#E0,#E1,#E8,#E9,#F0,#F1):LOCATE4,5:?CHR$(#E2,#E3,#EA,#EB,#F2,#F3)
90 LOCATE10,7:?CHR$(#F8,#F9,#FC,#FD):LOCATE10,8:?CHR$(#FA,#FB,#FE,#FF)

このプログラムは 882 バイトを消費し、1024 バイト中残りは 142 バイトである。
POKE を用いた場合よりは改善したが、今回のゲームの実装には足りなかった。

Base64の応用で埋め込む

配列を用いた埋め込みでは、2バイトを表すのに最低でも(数値1桁以上と区切り文字の)2文字を使い、(数値の桁が多く)3文字以上を使うことも多い。
Base64 形式を用いれば、3バイトを4文字で表すことができるので、より効率よくデータを埋め込みやすくなる。
ただし、Base64 そのままだと文字の種類によってエンコードするデータと文字コードの関係が変化し、デコードに手間がかかってしまう。
そこで、エンコードするデータの6ビットがそのまま文字コードの下位6ビットになる文字を使うことで、簡単にデコードできるようにした。

まず、キャラクターパターンデータを設定した状態で以下のプログラムを実行し、データを取得する。

IchigoJam BASIC
FORI=#700TO#7FF:?HEX$(PEEK(I),2);:NEXT:?""

取得したデータを CyberChef でエンコードする。

From Hex, To Base64 - CyberChef

エンコードした結果を埋め込み、さらにデコーダを実装すると、以下のコードになった。

IchigoJam BASIC
10 ' ヌルポ ガッ
20 [1]="@@@PDAL\DA@@`HB@<HbD`bdiQdQJL@@@@`HBNdQDN`@@@A@PDQXXF@A@PDC0bHRDJRUBPd40@@@B@`HDA@`0@@@G@@@A@`\L@NA@`@@@8A@PH@@CA@PC@@`DA@RH\L@@@@\@@@@A@0X@8BA@`@C0B@`P@@@@@@D@A@PDBAA``@@@D1@PDB@cH@[9FQXP"
30 [2]="GO@PHB@`HRhRDP@PDAC0GAK`@@@PDA@PD2@`EadYEa;0DA@`HB@aJaHQ@A@PDO@\Dn@@@@DA@S<A@PD@@`dE?@`HB@DB@`PHDB@@B@`HB@`PX@@@@@@@@@@BDP@@@@@@@@@PBP`@@@@@@0@PDB@`PH@@@@@@":P=#700:FORI=1TO2:Q=[I]
40 C=PEEK(Q):IFC-34D=PEEK(Q+1):E=PEEK(Q+2):POKEP,C*4|D>>4&3,D<<4|E/4&15,E<<6|PEEK(Q+3)&63:P=P+3:Q=Q+4:GOTO40
50 NEXT
60 VIDEO3:CLS
70 LOCATE0,0:?"11/15":LOCATE11,0:?"12345"
80 LOCATE4,4:?CHR$(#E0,#E1,#E8,#E9,#F0,#F1):LOCATE4,5:?CHR$(#E2,#E3,#EA,#EB,#F2,#F3)
90 LOCATE10,7:?CHR$(#F8,#F9,#FC,#FD):LOCATE10,8:?CHR$(#FA,#FB,#FE,#FF)

このプログラムは 734 バイトを消費し、1024 バイト中残りは 290 バイトである。
この形式であれば、残りの領域で今回のゲームを実装することができた。

今回のデコーダは、簡単のため、バイト数が3の倍数 (エンコードした文字数が4の倍数) のデータにのみ対応している。
そのため、データを配列領域の最初にまで書き込むことになる。
今回のプログラムでは使わない部分なので、問題にはならない。

ぬるぽガッゲーム

「ぬるぽ」が出たら、何かキーを押して素早く「ガッ」しよう!
でも、時々偽物が出てくるよ。
偽物のときは「ガッ」せずにスルーしよう!

10 ' ヌルポ ガッ ゲーム
20 [1]="@@@PDAL\DA@@`HB@<HbD`bdiQdQJL@@@@`HBNdQDN`@@@A@PDQXXF@A@PDC0bHRDJRUBPd40@@@B@`HDA@`0@@@G@@@A@`\L@NA@`@@@8A@PH@@CA@PC@@`DA@RH\L@@@@\@@@@A@0X@8BA@`@C0B@`P@@@@@@D@A@PDBAA``@@@D1@PDB@cH@[9FQXP"
30 [2]="GO@PHB@`HRhRDP@PDAC0GAK`@@@PDA@PD2@`EadYEa;0DA@`HB@aJaHQ@A@PDO@\Dn@@@@DA@S<A@PD@@`dE?@`HB@DB@`PHDB@@B@`HB@`PX@@@@@@@@@@BDP@@@@@@@@@PBP`@@@@@@0@PDB@`PH@@@@@@":P=#700:FORI=1TO2:Q=[I]
40 C=PEEK(Q):IFC-34D=PEEK(Q+1):E=PEEK(Q+2):POKEP,C*4|D>>4&3,D<<4|E/4&15,E<<6|PEEK(Q+3)&63:P=P+3:Q=Q+4:GOTO40
50 NEXT:C=10:W=5:S=0:VIDEO3:CLS:?" 1/15          0"
60 FORQ=1TO15:LC0,0:?DEC$(Q,2):A=RND(C+W)<W:C=C-!A:W=W-A:D=RND(7)*A+A:E=D&4:F=D*2&4:G=D*4&4:WAIT60+RND(60)
70 FORI=0TO1:J=I*2:LC4,4+I:?CHR$(#E0+J+E,#E1+J+E,#E8+J+F,#E9+J+F,#F0+J+G,#F1+J+G):NEXT:CLK:CLT
80 T=TICK():IF!INKEY()IFT<120GOTO80ELSEGOTO110
90 LC10,7:?CHR$(#F8,#F9,#FC,#FD):LC10,8:?CHR$(#FA,#FB,#FE,#FF):IFAVIDEO4:S=S-600+T*5:ELSES=S+120-T
100 LC10,0:?DEC$(S,6):WAIT60+60*A
110 FORI=0TO1:LC4,4+I:?"      ":LC10,7+I:?"    ":NEXT:VIDEO3:NEXT

OneFiveCrowd で実行する / IchigoJam web で実行する

「ぬるぽ」に対して「ガッ」をした様子

  • 10行目:FILES 対応のタイトル
  • 20~30行目:フォントデータと、デコードの開始
  • 40行目:フォントデータのデコードとキャラクターパターン領域への書き込み
  • 50行目:出題する本物の数 C・偽物の数 W・スコア S・画面の初期化
  • 60行目:出題処理
    • 問題カウントの表示の更新
    • 本物を出す (A=0) か偽物を出す (A=1) かの決定
    • 本物・偽物の残り数の更新
    • 偽物の場合に変える文字の決定
    • ランダムな時間待つ
  • 70行目:「ぬるぽ」または偽物を表示し、時間の計測を開始する
  • 80行目:キー入力およびタイムアウトの判定を行う
  • 90~100行目:キーが押された時の処理
    • 90行目:「ガッ」を表示し、画面の反転と得点の減算 (偽物時) または得点の加算 (本物時) を行う
    • 100行目:得点の表示を更新し、少し待つ
  • 110行目:「ぬるぽ」または偽物と「ガッ」の表示を消し、画面の反転を解除する

大石泉すきゲーム

「ぬるぽガッゲーム」の表示する文字だけを差し替えて、「大石泉すきゲーム」も作ってみた。
「大石泉」が表示されたら素早くキーを押して「すき」し、偽物は無視しよう。

10 ' オオイシイズミ スキ ゲーム
20 [1]="@@@@@@A?@@B@`HB@`O>@`@DA@`HDBAA`PD@`HA@HA@L@@@@@@G<@@HBPbHRD?8B@@PDB@`PHDFA@PB@`D@`D@0@?@PDA@`HG@O<@@@@@@O0DC@0TIDPG@@PDA@PDAO0@@PDA_0HB@`P@@@C>@@@@@@\LEBQDA@\@>@`HB@`H>@@@@@<HB@<HBDB@>@`H"
30 [2]=">@`HC0@>@`PXX@G:`lRhdH2C`@@AG1HR_0@D`@C<IBS?HDPIM`@DBAA`@HRhdHbD`8B@@@@@O0@@@0Q@PG;@PDA@0@PDA@L@@@@CPDC@PDA@`@@A@PD_@@@?@@@@\HB@>D@`@@\HDA@L@0@`4C@@@@C@@@@@":P=#700:FORI=1TO2:Q=[I]
40 C=PEEK(Q):IFC-34D=PEEK(Q+1):E=PEEK(Q+2):POKEP,C*4|D>>4&3,D<<4|E/4&15,E<<6|PEEK(Q+3)&63:P=P+3:Q=Q+4:GOTO40
50 NEXT:C=10:W=5:S=0:VIDEO3:CLS:?" 1/15          0"
60 FORQ=1TO15:LC0,0:?DEC$(Q,2):A=RND(C+W)<W:C=C-!A:W=W-A:D=RND(7)*A+A:E=D&4:F=D*2&4:G=D*4&4:WAIT60+RND(60)
70 FORI=0TO1:J=I*2:LC4,4+I:?CHR$(#E0+J+E,#E1+J+E,#E8+J+F,#E9+J+F,#F0+J+G,#F1+J+G):NEXT:CLK:CLT
80 T=TICK():IF!INKEY()IFT<120GOTO80ELSEGOTO110
90 LC10,7:?CHR$(#F8,#F9,#FC,#FD):LC10,8:?CHR$(#FA,#FB,#FE,#FF):IFAVIDEO4:S=S-600+T*5:ELSES=S+120-T
100 LC10,0:?DEC$(S,6):WAIT60+60*A
110 FORI=0TO1:LC4,4+I:?"      ":LC10,7+I:?"    ":NEXT:VIDEO3:NEXT

OneFiveCrowd で実行する / IchigoJam web で実行する

「大石泉」に対して「すき」をした様子

まとめ

  • 東雲フォントは、JIS の文字コードと十六進数で表した文字の形の対応を記述したテキストファイルの形式で配布されている
  • Base64を応用し、文字コードの下位6ビットでそのままデータの6ビットを表すエンコードを用いることで、256+2バイトのキャラクターパターンデータを効率よく (506バイトで) 埋め込み、残りの容量でゲームを実装することができた
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?