
2026/07/20 0:20
C64 バージョンのバシック言語ダンジョンクローラー:ゴブリン襲撃(C64 バージョンの一部8)
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
パートエイトは、このコモーダ BASIC ゲームにとって決定的な進化を意味し、核心的な安定性の問題を取り除き、コアエンジンをアップグレードする。最も重要な変更は、静的な敵を、平行アレイ(GH(10)、GX(10)、GY(10))を用いてプレイヤーを追跡しながら、健康状態と位置を追跡し、メモリー腐敗を回避する知的ゴブリンに置き換えることであり、これは以前のプロトタイプで発生した主要なバグであるアレイの成長が 4K の文字セットと衝突するという問題に対処するためのものである。厳格な 16K メモリ制約および可変切断の問題に対処するために、この更新では BASIC キーワード(例:"GO")との競合を回避する短い二文字の変数(QK、AK、OK、PK、FK)を使用し再定義可能なコントロールを実装しており、これにより
GET を通じた動的キーマッピングが可能となり、文字セットの沈黙した上書きを防ぐ。将来の更新では、ゴブリンのカウンターアタック、複数の敵タイプ、霧の戦い、およびレベルシステムが約束されており、これは単一レベルのプロトタイプから、4K BASIC 環境内での現代的なエミュレーターサポートとともに、公平な勝ち負け条件を持つ完全なアドベンチャーへとプロジェクトを拡張する。本文
追跡するゴブリンとメモリバグ:RGC シリーズ第 8 部
はじめに
前回の第 7 部ではダンジョン全体にゴブリンが湧いていましたが、彼らは配置された場所から一歩も動きませんでした。プレイヤーが攻撃しても去っていくだけでした。 今回からは、彼らがプレイヤーを追いかけてくるようになりました。 コードの小さな変更ですが、ゲームの「感触」における大きな前進です。
その過程で、キャラクターセットを破損させる奇妙なメモリバグに巻き込まれてしまいました。このバグこそがこのシリーズからの最大の教訓であり、軽くスルーせず、必ず目を通してください。
オンラインエディタへのアクセス ブラウザ上でソースコードを直接編集・実行できます(RGC オンライン IDE:
)。ダウンロードや設定は一切不要です。part8d.bas
1. 前回までのあらすじ
第 7 部の着地点。
- 独自のカスタム文字セットを読み込み。
- ランダム生成された 3x3 メタタイルを基盤とするマップを表示。
- D&D 風ステータスを備えたプレイヤーと、PEEK コリジョン検出システムを実装。
- プレイヤーが隅に追い込まれた際に機能するテレポートキーを搭載。
しかし欠けていたのは「緊張感」。
- 誰か脅かすものもなければ、勝敗やリスクも存在しませんでした。
この部分で変化する 4 つの要素
- キー操作のカスタマイズ:
という設定は万人の味ではありません。QAOP - ゴブリンのゲームオブジェクト化: 位置情報(X/Y)、健康値(HP)、および簡易 AI を装備。
- 本格 d20 ロールによる戦闘: 筋力修飾子を考慮した攻撃判定を実装。
- 自己書き換え問題の解決: 後述するほど複雑なメモリ問題を解消。
2. キー操作のカスタマイズ
QAOP は幼少期から育った人々にとっては標準ですが、それ以外の層には非標準的です。両方の立場が正しいため、「どちらかを選ぶ」のではなく、「ゲーム側が文字通りのキー(リテラル)を参照するのをやめる」のが解決策です。
デフォルト値の格納
REM デフォルトのキー設定 QKEY$="Q" AKEY$="A" OKEY$="O" PKEY$="P" FKEY$="SPACE"
重要なのは最初の 2 文字だけです。 コモドール BASIC は変数名を厳密に 2 バイトしか保存しません(例:
PLAYERHEALTH の内部では PL しか確認しない)。これにより、非常に不思議でデバッグが困難な挙動を引き起こしてしまいます。
同様に、キーワードが含まれるとトークン化エラーになります。
REM GL は GOLD にできない - GO が GOTO と衝突する (2 文字の変数名) GL=0
SCORE(OR を含む)、COUNT(ON を含む)など、変数名の中に BASIC のキーワードが含まれると構文エラーが発生します。
キー入力ルーチンの変更
入力チェックを「文字比較」から「変数比較」へ変更します。
IF P$=OKEY$ THEN PY=PY-1 IF P$=PKEY$ THEN PX=PX+1 IF P$=QKEY$ THEN PY=PY-1 IF P$=AKEY$ THEN PY=PY+1 IF P$=FKEY$ THEN GOSUB FIRE
入力ルーティン:GET の利用
キーリマップ画面では
INPUT ではなく GET を使用します。
- INPUT: ユーザーが入力して RETURN を押すまで待機(パニックを引き起こしやすい)。
- GET: キーストロークバッファから単一の文字を即時に読み取り。
キー待ちのループ処理:
CHANGETHISKEY: GET K$: IF K$="" THEN GOTO CHANGETHISKEY RETURN
これは「キーの有無チェック」です。選択したキーを押した後で RETURN を押してもらうのは誤った結果を招きます。
キー設定画面の実装
CHANGEKEYS: PRINT "{CLR}{GREY}" PRINT "キー変更" PRINT "===========" ' 上方向 PRINT "デフォルト: QAOP スペース" PRINT "上方向のキーを押してください" GOSUB CHANGETHISKEY : QKEY$=K$ ' 下方向 PRINT "下方向のキーを押してください" GOSUB CHANGETHISKEY : AKEY$=K$ ' 左方向 Print "左方向のキーを押してください" GOSUB CHANGETHISKEY : OKEY$=K$ ' 右方向 Print "右方向のキーを押してください" GOSUB CHANGETHISKEY : PKEY$=K$ ' 攻撃 (FIRE) Print "攻撃 (FIRE) のキーを押してください" GOSUB CHANGETHISKEY : FKEY$=K$ ' 確認画面 Print "設定で満足しましたか?(Y/N)" HOLDFORKEY: GET A$ IF A$="" THEN GOTO HOLDFORKEY IF A$="Y" THEN RETURN IF A$="N" THEN GOTO CHANGEKEYS RETURN
注意点: ラベル名(
など)はオンライン IDE の利点です。物理マシンでは行数に変換されるため、読みづらくなります。CHANGEKEYS
3. 追跡するゴブリンの実装
これまでのゴブリンはプログラム上には実体を持たず、単なる装飾品でした。移動させるためには、個体の管理(位置・HP)が必要です。
アレイによる管理
REM 敵変数の宣言 GC=0: REM GOBBO カウント(ゴブリン数) DIM GH(10) : REM GOBBO HP アレイ FOR GC=1 TO 10: GH(GC)=10: NEXT GC GC=0 DIM GX(10) : DIM GY(10) : REM GOBBO リスト
- GX / GY: ゴブリンの X/Y 座標を保持。
- GH: HP を保持。
- GC: 実際に配置した数をカウント。
メタタイルからの追加:
ADDGOBBO: REM ゴブリンリストに追加 - GX=X, GY=Y (PX/PY と一致) REM ガード:10 で止めて GC が DIM GX(10) を超えないようにする IF GC>=10 THEN RETURN GC=GC+1 ' 中央座標へ修正(左上ではなく 3x3 ブロックの中央) GX(GC)=COL+1 GY(GC)=ROW+1 RETURN
※
COL+1 と ROW+1 は、ゴブリンが描画されている中央位置を指します。
敵 AI と移動ロジック
ゲームはターン制です(プレイヤー動作 → 敵動作 → チェック)。 単純にプレイヤー方向へ移動するロジック:
ENEMYLOGIC: IF GC=0 THEN RETURN FOR G=1 TO GC REM デッドなゴブリンをスキップ IF GH(G)<=0 THEN GOTO NEXTENEMY ' プレイヤー方向へずらす IX=GX(G) : IY=GY(G) ' X 座標調整 IF GX(G)>PX THEN IX=GX(G)-1 ' プレイヤーより右 -> 左へ移動 IF GX(G)<PX THEN IX=GX(G)+1 ' プレイヤーより左 -> 右へ移動 ' Y 座標調整(プレイヤーより下 -> 上へ) IF GY(G)>PY THEN IY=GY(G)-1 IF GY(G)<PY THEN IY=GY(G)+1 NEXTENEMY: NEXT G RETURN
重要: 移動先(
IX, IY)と現在地(GX, GY)を分離して保持し、移動許可後に位置を更新します。
コリジョンチェック: ターゲットセルに画面コード 46(床の点)が含まれていれば空で、ゴブリンは移動可能です。壁やプレイヤーがいないかを確認します。
MOVEENEMY: REM コース位置を古いゴブリンセルに設定して消去 GOSUB ERASEENEMY REM 新しいセルに移動して描画 GX(G)=IX : GY(G)=IY GOSUB DRAWENEMY RETURN
FOR ループ内のラベル: COMODORE BASIC の
コマンドが存在しないため、CONTINUEの直前にラベル (NEXT) を置き、ループをスキップします(NEXTENEMY:を使用)。GOTO
4. 戦闘ロールの実装
ゴブリンが移動後にプレイヤーに隣接した場合や、意図的に衝突させた場合に戦闘が始まります。
距離判定 (ABS
の使用)
ABSIF ABS(GX(G)-PX)<=1 AND ABS(GY(G)-PY)<=1 THEN GOSUB COMBAT
ABS は符号を剥ぐため、両軸とも 1 マス以内であれば(斜め含む 9 マス内)、戦闘を開始します。
コマンド処理
COMBAT: REM d20 ロール R=RND(-TI) D20=INT(RND(1)*20)+1 REM 筋力の修飾子を追加 MOD=INT((STATS(0)-10)/2) D20=D20+MOD REM ゴブリンの AC(8)に命中したか判定しダメージを決定 IF D20<=8 THEN MSG$="MISS!":GOSUB ALERT: HP=HP-5:GOSUB SHOWHUD:RETURN ' 攻撃成功時のダメージ計算 DMG=INT(RND(1)*8)+1+MOD MSG$="ゴブリンに "+STR$(DMG)+" のダメージを与えた!":GOSUB ALERT ' HP 減算と死亡処理 GH(GC)=GH(GC)-DMG IF GH(G)<=0 AND G>0 THEN MSG$="ゴブリン死んだ!":GOSUB ERASEENEMY:GOSUB ALERT RETURN
- d20 ロール:
で 1〜20 の乱数を生成。INT(RND(1)*20)+1 - 能力値:
は筋力で、+3 などと計算される修正値を取得。STATS(0) - 命中判定: ゴブリンの AC(8)より小さいとミスし、ダメージを受けると同時に攻撃者が
ダメージ。-5
5. カスタム文字セットを食い尽くしたバグ
ゴブリンのアレイを追加すると、画面が乱码になり、カスタム文字セットが破損しました。これはいわゆる メモリ割り当ての衝突 です。
問題の背景
- VIC-II は一度に 16K(アドレス 0〜16383)のみを表示可能。
- デフォルトでは下部 16K を使用しているが、文字セットをここに置くと BASIC プログラムと衝突する。
- 第 7 部の問題点: 4K の文字セット全体を読み込み、大文字セットの半分(未使用領域)まで BASIC に侵入していた。
アドレスマッピング比較表
| アドレス範囲 | 第 7 部 (4K セット) | 第 8 部 (2K セット) | 状態 |
|---|---|---|---|
| 2049〜 | BASIC プログラム、変数、アレイ | 同じく | プログラムが上へ成長 |
| 12288 | 文字セット開始 (衝突) | 文字セット開始 | 旧セットは成長をブロック |
| ~14336 | BASIC 用の空き領域 (浪費) | 文字セット (未使用小文字セーフ) | 第 7 部ではスペースが浪費 |
| 14336 | - | 文字セット開始、BASIC はここで止まる | 新しい 2K セットは安全な場所 |
| 16383 | VIC-II の終了 | VIC-II の終了 | 最大可視メモリ |
解決策:文字セットのトリミングと移動
- 必要な大文字セットのみ(2K)を読み込み、アドレスを
に設定。14336 - ファイル名:
→CHARS.BIN
(ファイルサイズは半減)。CHARS8C.BIN
VIC-II のレジスタ変更
画面メモリと文字メモリの境界を変更します。
POKE 53272,(PEEK(53272)AND240)+14
:現在の値を読み取る。PEEK(53272)
:下位 4 ビットをマスク(画面メモリ部分を残す)。AND 240
:文字セットの新しいオフセット(14 / 2 = 7 → 14336)を設定。+14
BASIC のメモリ制限の設定
BASIC に「ここで停止する」と指示し、新たな領域で動作させるようにします。
REM メモリ制限対策: IF PEEK(2)<>73 THEN POKE 55,0:POKE 56,56:POKE 2,73:CLR
: メモリポインタを 14336 に設定。POKE 55,0 : POKE 56,56
: 修正フラグを設定(73 は恣意的な値)。POKE 2,73
: 必須。BASIC は内部の帳簿を再計算させる必要があるため、ポインタ変更後に実行。CLR
再起動時の処理ロジック
IF PEEK(2)<>73 THEN POKE 55,0:POKE 56,56:POKE 2,73:CLR REM カウンタで初期化状態を管理 IF A=0 THEN PRINT CHR$(147)"読み込み中、お待ちください" A=A+1 IF A = 1 THEN GOTO CHARS ' 文字セットを読み込む IF A = 2 THEN GOTO SETCHARS ' ポインタと制限を設定 CHARS: LOAD "CHARS8C.BIN",8,1 SETCHARS: POKE 53272,(PEEK(53272)AND240)+14
- フラグ管理の注意点:
は全てをリセットするため、変数CLR
もゼロに戻されます。しかし、修正フラグであるアドレス 2 の値(A
)はPEEK(2)=73
に影響されません。そのため、電源オフを除き、一度設定すれば再起動時も維持されます。CLR - 冷起動時のリスク: アドレス 2 に偶然 73 が含まれる確率は低く、修正が実行されない場合、文字セットを食い尽くす可能性があります。
教訓: 8 ビットマシンではメモリは共有資源であり、BASIC、VIC-II、KERNEL、そしてコードがすべて同じ 64K で動作します。誰かが足を撃つのを止めません。
6. 次に何をするか
現状の
FIRE: サブルーチンには頭方向の処理しか含まれていません。今後のアップデート計画:
- ダメージを与えるゴブリン: 単純な接触ではなく、攻撃行為自体を実装。
- 公平な敵生成: ランダムマップでのゴブリン配置バランス調整。
- 勝敗条件: 健康値が負になった際の処理(ゲームオーバー)の実装。
- 複数の種類の敵: 「プレイヤーへ向かう」以外の移動パターン追加。
- 霧の戦争 (Fog of War): ダンジョン全体を一度に表示せず、視界制限の実装。
- 階段と階層: 新たなエリアへの移動ロジックを追加。
プログラム全体は IDE で確認・改変可能です(
part8d.bas)。ゴブリンの AC を変更したり、知能レベルを上げたりして試してみてください。