この記事って?
MATLABマリオに触発されて、MATLABでFC版ゼルダの伝説を一部再現した話です。
- やったこと
- プレイヤーキャラクター(リンク)の移動
- プレイヤーキャラクター(リンク)の攻撃 ※剣のみ
- 衝突判定の実装
- エリア移動&洞窟への出入り
- 敵キャラ(オクタロック)のスポーン
- 敵への攻撃時および敵からの攻撃時のダメージ表現
- Joy-Con操作
- やれてないこと
- 盾による防御
- 剣ビーム
- 剣以外のアイテム使用
- ダンジョン
- オクタロック以外の敵キャラ(ボス含む)
- 剣取得等のイベント
- タイトルやポーズ画面
環境
- Surface Pro 8
- Windows 11 Home
- MATLAB R2024b (Home)
フォルダ構成
📁 The Legend of Zelda (NES)
┣ 📁 ClassDef
┣ 📁 Images
┣ main.m
┣ getPlayerOperation.m
┗ TheLegendOfZeldaNES.prj
main.mを実行すればゲームが起動します。実行後、キーボードの"w", "a", "s", "d"を押すことで上下左右に移動し、マウスクリックで剣を振ります。Joy-Con接続していれば、Joy-Conで操作することもできます(その際はスティック操作で移動し、Aボタンで攻撃します)。
ClassDefには各種クラスの定義を、Imagesには各種画像を格納し、それぞれへのパス追加はプロジェクトファイルを開くことで実行する構成です。※クラス分けについては、これがオブジェクト指向初挑戦なので何卒温かい目で見てください…。
ClassDef内のファイル構成
📁 ClassDef
┣ 📁 matlab-hidapi-master
┣ UI.m
┣ HUD.m
┣ Field.m
┣ PlayerCharacter.m
┣ Enemy.m
┣ NonPlayerCharacter.m
┣ JoyController.m
┣ mapPolygon.xlsx
┣ cavePolygon.xlsx
┗ posEnemy.xlsx
matlab-hidapi-masterについては、Joy-Con接続のためのファイルで、リンク先よりダウンロードできます。
mapPolygon.xlsxおよびcavePolygon.xlsxについては、それぞれ衝突判定用および洞窟進入判定用のpolyshapeを生成するための座標点を記載したファイルです。posEnemy.xlsxについては各エリアのどの座標に敵をスポーンさせるかといった情報を格納しています。
Images内のファイル構成
📁 Images
┣ Miscellaneous - HUD & Pause Screen.png
┣ Overworld - Overworld (First Quest).png
┣ Playable Characters - Link.png
┣ Enemies & Bosses - Overworld Enemies.png
┣ Miscellaneous - Enemy Death.png
┗ Miscellaneous - NPCs.png
それぞれのSprite Sheetについては後述するサイトからお借りしました。いろいろな素材が1枚の画像にまとめられており、大変使いやすかったです。
追記
2026/01/11: GitHubで公開しました。
偉大なる参考リンクの方々
↓ベース
↓Joy-Con接続
↓Sprite Sheet(特にお世話になりました。これがなければ作れませんでした…)
あわせて読んでいただきたい記事
説明が長くなりそうなものや、ネタとして使えそうなものは別途記事にして紹介しております。基本は下記リンクで紹介しているので、本記事での補足はサラッとしたものにしようかと思います。
コード紹介と補足
main
- 初期化→メインループ→Break処理の流れです
- メインループ内は、 操作情報の取得→リンクの行動→敵の行動→接触/攻撃判定→エリア移動判定→操作情報の取得…という流れです
- 無限ループの組み方については、リンク先で軽く触れています
getPlayerOperation
Joy-Con接続をされているかどうかをhid.isOpenで判定し、Joy-Con接続がされている場合はJoyControllerクラスのメソッドを実行し操作情報を取得します。Joy-Con接続がされていない場合はfig.UserDataで操作情報を取得します(fig.UserDataはキー操作またはクリック操作でコールバック関数を実行して更新します)
UI
-
uifigureとuipanelを作成してるだけで大したことはしてないです。コールバック関数の定義もこのクラスのコンストラクタの中で行っています -
KeyPressFcnだけでなくKeyReleaseFcnも使っています、これはKeyPressFcnだけだと斜め移動の際の操作性がよろしくなかったからです。例えば右上へ移動するシーンを考えたとき、操作としては右("d"キー)を押しながら、上("w"キー)を押したり離したりすることで移動する操作もあると思います。KeyPressFcnの仕様として最後に押したキーボードに対して処理がされるため、"d"キー押す→"w"キー→押す→"wキー"離す操作をすると、"d"キーを押しているにも関わらずそれ相当の処理がされなくなり、"w"キーを話した時点でキャラクターが止まってしまいます。なので、bufferKeyというものを設けて前の操作を保存しておくようにしています。KeyReleaseFcnではbufferKeyに格納した前の操作をそのまま採用するかどうかを判定しています(参考リンクはこちら)
HUD
ここでは、HUD用のスプライトシートを使って画面を作ってます。ミニマップはそれ用にaxesとrectangleを使って作成し、ハートアイコンについてはセル配列にimage関数でオブジェクトを作りました。
Field
- リンク先から借用した地上の全体画像を描画してフィールドを作成しています。このとき計算がしやすいよう、
axes上の1がキャラクター1マスに相当するような大きさでフィールドを描画しました - 衝突判定用や洞窟判定用の
polyshapeについてもここで作成しています。それぞれExcelファイルをインポートして、その配列を頂点座標として作成およびプロットしています - エリア移動については
XLim,YLimを徐々に移動させることで画面スクロールっぽい動きにしています - 洞窟に出入りする際の暗転については、
recBlackScreenLeftおよびrecBlackScreenRightを使っています。普段は1 pixで画面端に待機させていますが、洞窟判定に触れたら(intersectの出力が多角形だったら)徐々にwidthを増やすことで幕が閉じるような演出にしています。また、幕が閉じている間にXLimとYLimを変更して場面転換っぽくしています
PlayerCharacter
- 移動やモーションについては、こちらで紹介している記事をご参照ください
- 攻撃判定用の
polyshapeも作成しています。最初に作るだけ作って、普段は画面外で待機させていますが、攻撃モーション中にはリンクの剣に重なる位置へ移動させています。このpolyshapeに敵が触れる(intersectの出力が多角形になる)ことでダメージ時のメソッドがEnemy側で実行されます
Enemy
- 行動AIについては超簡易的です。プレイヤーへ向かって移動し、プレイヤーがダメージ中は離れる方向へ移動します
- 移動の仕様(特に衝突判定)についてはPlayerCharacterとほぼ同じです
- プレイヤーと違う点は、スポーン時のアニメーションと、剣で切られたときの消滅アニメーションです。詳細はこちらで紹介しているアニメーションの記事をご参照ください
- どのエリアのどの座標に敵を湧かせるかは、管理Excelをインポートして判断しています。各エリアに対応したセルへ2桁の16進数を書いており、その16進数を使って判定しています。例えば、A1のセルに
65と書いてあったら、一番左上のエリアに移動したタイミングで左から6マス、下から5マスの場所に敵をスポーンさせるという流れです(これらの処理はField.mのchangedAreaメソッド内で行っています)
NonPlayerCharacter
これについては、おじいちゃんを配置してるだけなのであまり大したことはしてないですね…。
JoyController
- カルマンフィルターで姿勢推定するメソッドがありますが、使用してないです
- 詳細はこちらで紹介しているJoy-Con接続の記事をご参照ください
