2026春休み開発日記(2)

2月8日(日)

開発日記が長くなってきたので2に切り分けることにした。

今朝はOmarchyで使っているyt-dlpの調子が悪く、Geminiに相談したらアップデートを薦められた。

sudo pacman -Syu yt-dlp

何も考えず、実行。ここから地獄が始まった😢

コンソールに凄まじいログが流れだしてyt-dlpをアップデートするのにそんなにやることあるのかなぁと不安に。どうやらOmarchy含むArch系のLinuxでは依存関係で不具合を起こさなように、全体をアップデートする思想らしい。無知とは恐ろしい。しかもGeminiもひどいよね。sudo pacman -Syuだけなら危険を感じられたんだけど、yt-dlpが後ろにくっついているからyt-dlpだけアップデートするんだなぁと思っちゃうじゃんね!確認しなかった自分がいけないけど、ストレスは発散させてもらおう😁

そのあとチャッピーと相談しながら、なんとか元に戻った。さよならGemini。

気を取り直して、今日はDTDに新しいステージを追加してみようと思う。公開するゲームではないから版権気にせず何でもありである。子供の頃、大好きだった?FF6のステージを作ることにした。

ステージ用のjsonデータの構造は次の通り。ここに、背景画像とBGMの指定を追加する。

{
  // 問題集・クイズデータの参照パス
  "QuestionCollectionPath": "QuestionCollections/genshin_impact",

  // 背景画像
  "Background": {
    "Default": {
      "ImagePath": "Stages/genshin_impact/bg_stage01"
    },
    "WaveOverrides": [
      {
        "WaveNumber": 3,
        "ImagePath": "Stages/genshin_impact/bg_stage01_night",
        "Transition": {
          "Type": "CrossFade",
          "DurationSec": 1.2
        }
      },
      {
        "WaveNumber": 5,
        "ImagePath": "Stages/genshin_impact/bg_stage02",
        "Transition": {
          "Type": "Scroll",
          "DurationSec": 1.0,
          "Direction": "Left",
          "DistancePx": 800
        }
      }
    ]
  }

  // BGM
  "Bgm": {
    "Default": {
      "SoundName": "オーバーライド"
    },
    "WaveOverrides": [
      {
        "WaveNumber": 5,
        "SoundName": "Bling-Bang-Bang-Born"
      },
      {
        "WaveNumber": 6,
        "SoundName": "bossboss!"
      }
    ]
  },

  // 敵側の拠点情報
  "EnemyCastle": {
    // 拠点画像のパス
    "CastleImagePath": "Castles/castle_takenoko",

    // 拠点のステータス定義
    "CastleStatus": {
      "Name": "Takenoko Castle",   // 拠点名
      "Lv": 1,                     // 拠点レベル
      "Hp": 1000,                  // 拠点HP
      "Gold": 100,                 // 拠点撃破時などの獲得ゴールド
      "Exp": 100                   // 拠点撃破時などの獲得経験値
    }
  },

  // 通常敵ユニットの出現定義(複数可)
  "Enemies": [
    {
      "UnitName": "Kuribo",        // 敵ユニットID / 名前
      "SpawnWave": 1,              // 出現Wave番号
      "SpawnFrequency": {
        "Cooldown": 5,             // 出現間隔(秒など)
        "RandomRange": 0           // 出現間隔に加算される乱数幅
      }
    }
  ],

  // ボスユニットの出現定義
  "Bosses": [
    {
      "UnitName": "Dragon",        // ボスユニットID / 名前
      "SpawnWave": 5               // 出現Wave番号
    }
  ],

  // Wave進行ルールの定義
  "Waves": [
    {
      "WaveNumber": 1,             // Wave番号
      "NextTrigger": "HP",         // 次Waveへ進む条件種別
      "NextTriggerValue": 9999     // 条件の判定値(例:拠点HP)
    }
  ]
}

2月9日(月)

今回のDTDの開発はOmarchy(Arch Linux)+Cursorでスタートしているが、Cursorは課金の問題とMSからの拡張機能の嫌がらせを受けているので、余り使いたくない。だけど、AIが有能すぎて離れられない。どうしたものか😢

Cursorは年間契約していて5月までは使える。とりあえず、インテリセンスが動かないのは困るので、動くようにしよう!

詳細はObsidianにまとめておいたが、cursor.com謹製のC#拡張機能をインストールして、パッケージマネージャーでコミュニティ製(boxqkrtmさん)のUnity Editorパッケージをインストール。初設定をすればインテリセンスが動く!

インテリセンス

Unityのバージョンを上げたら(2022→2023)、古いメソッド利用の警告がでた。Object.FindObjectOfType<T>()FindAnyObjectByType<T>()に置き換えた。

2月10日(火)

ゲームの解像度を変更しようと思う。カッコ内は16×16を1個とした場合の個数

  • ファミコン:256×240(16×15)
  • スーパーファミコン:256×224(16×14)

比率で言うと1:1に近い。これをTV(4:3)に描画するということは横方向が1.3333倍になる。つまり16×16想定の正方形っぽいものを描くのであれば16×12でデータを作成しておくと、画面に表示した時正方形っぽくなるわけだ。なんか難しいね😢昔のゲームデータは横に1.3333倍に伸ばされる想定で描いていた。なので、そのままのデータを現代のモニタに表示すると縦長に感じるってことだ。厳密なレトロゲー作るなら考慮するが、今回のDTDではそこは無視することにする。

一方、パソコンの画面は16:9が主流で解像度は色々ある。

  • 320×180(1倍)
  • 640×360(2倍)
  • 1280×720(4倍)
  • 1366×768(4.26倍)chromebookなどで見かける
  • 1920×1080(6倍)

16ドットベースで考えるとチップの数は

  • 320×180=20×11.25
  • 640×360=40×22.5
  • 1280×720=80×45

1280×720が良さそう。

ドットを4倍に引き伸ばすとすると、縦がキレイに治まらず余白が出る。

  • 1280×720=20×11+余白

最大化した場合横はぴったりキレイに拡大され、上下に余白が入るはず。

ということで、仮想画面を320×176、実画面を320×180とすると、フルスクリーンにしたときに、仮想画面1280×704(20チップx11チップ)となり、実画面の1280×720に表示すると上下に8ドットずつの余白が出来る画面となる。

2月11日(水)

建国記念日。祝日だが教室はあるのだ。朝の時間に出来ることをしよう。

昨日、画面解像度について詰めた結果、ユーザーの画面解像度を1280×720と想定した場合、ゲーム世界を320×176の解像度で作ると、16×16のマップチップ換算で20チップx11チップの世界になる。これが想定画面に拡大されると4倍に引き伸ばされ、1280×704となり、上下に8ドットずつの余白が出来る想定だ。

この想定で作る場合、UNITYの各設定が重要となる。Geminiに聞いた現時点でのベスト設定。

画面・カメラ

  • UI Scale Mode: Scale With Screen Size
  • Reference Resolution: X: 640, Y: 360
  • Screen Match Mode: Match Width or Height
  • Match: 1 (Height) ※縦の解像度(180ドットの比率)を基準に合わせるため
  • Upscale Render Texture:OFFフルスクリーン表示しても動きは補完されヌルヌル動く
  • Pixel Snapping:ON

スプライト

  • Pixels Per Unit (PPU):16

ちょっと怪しいのでChatGPTにレビューさせると次のようになった。

■ 内部解像度

  • 320×176
  • 16×16チップ換算:20×11

■ 拡大

  • 4倍整数拡大 → 1280×704
  • 余り 16px → 上下 8px 黒帯

■ Unity設定(ゲーム側)

Pixel Perfect Camera

  • Assets PPU = 16
  • Reference Resolution = 320×176
  • Pixel Snapping = ON
  • Crop Frame = ON(上下黒帯を出すため)

■ UI(文字のみ)

  • 別Canvas
  • Scale With Screen Size
  • Reference Resolution = 1280×720
  • 黒帯部分にはUIを置かない

とりあえず、作ってみたが、ちょっと見た目が大きくなりすぎるなぁ。

2月12日(木)

倍のサイズにしてみた。ので比較してみる。

元々のサイズ – DTD
640×352 – DTD
320×176 – DTD

比べてみると…320x176 はファミコンっぽさはあるが、タイピング用の画面などを追加していくとちょっと狭いし、特大キャラなどを配置すると画面が小さい気もする。かと言って、倍のサイズだと、ちょっと細かすぎる。広すぎる気もする。うーむ☹️

戦闘画面ではWorldSpaceに塔やキャラを配置して動かすが、塔の上にHPを数値表示したい場合、これまで通りCanvas+Text(レガシー)を使いがちである。しかし、CanvasはScreen Space基準のレイヤーであり、WorldSpaceのオブジェクトとは座標系が異なるため、キャラクターのスプライトとテキストを同じ階層にまとめることができない。また、位置合わせの処理も必要になる。

かと言って、塔そのものをCanvasのレイヤーに配置することもできない。塔は当たり判定や描画順を含めたWorldオブジェクトであり、UIレイヤーに移すことは設計として適切ではない。

ということで、レガシーなTextは使わずにTextMeshPro(3D)を使ったほうが良さそうだ。これであれば文字もWorldSpaceのオブジェクトとして扱えるため、キャラクターの子オブジェクトにでき、プレファブとしてもまとめられる。

2月12日(木)

サボったので土曜日に書いている😁

毎パソの画面デザインを調べてみた。

表示エリア・入力エリアのボックスサイズは 686 x 25835文字 x 11行 だった。フォント情報は下記の通り。行間が135%というのがなんとも…。

line-height: 135%;
font-family: 'Noto Sans JP';
font-weight: 500;
font-size: 17px;

2月13日(金)

サボったので、土曜日に書いている😂

DTDの解像度が決まらずズルズルしていたけど、そろそろ決着をつけるべく、取り組んでみた。

レトロゲームの解像度

結構な長文になったのと、良い感じにまとまったので、別記事として保存しておく。

2月14日(土)

今日は画面のことを詰めていこうと思う。検討することを列挙してみる。

  • アスペクト比:
  • 画面解像度:
  • フォントの種類
  • シーン毎のカメラ構成・設定

なかなか大変だ。

フォントの種類

ワールドスペース内のステータス表示は「ちびちび」

タワーの上に表示するHPやワールドスペース内に表示する数字・英字(日本語なし!)に関してはドット感が出やすい ちびちび を使おうと思う。下記のサンプルはProcessingで作ったため、穴が埋まってしまっている。UnityのTMPではどうなるか??

UIメインのデッキ編成などの画面は「JFドット東雲ゴシック14」

デッキ編成画面のステータス値やキャラクター名はドット感もありつつ日本語に対応していて、そこそこ読みやすいもの。ということで 美咲ゴシック(8px)にする?それとも、JFドット東雲ゴシック14(14pt)にする?書いていて思ったけど、マップチップが16pxだから行間を2pxとすると JFドット東雲ゴシック14 がベストなのでは?

毎パソ用の入力欄は「Noto Sans JP」

一昨日調べた通り、毎ぱそは Noto Sans JP を使っているので、それを使おう。まぁ、毎パソは組み込まず、本家の結果をインポートしてガチャチケットを配布してもいいか。

メニューボタンなどは「マリオ風」?

初期バージョンで使用していたマリオ風にするか、ドット絵を強調していくか。悩ましい。

2月15日(日)

クレーの大冒険を作っていたときにTMPを使っていたことを思い出した。

DTDでも使いたくなってきた(おい

脱線しまくりである。

作ったフォントをここに残す。

SNchibiフォントの作成 – Aseprite

本日の作業用BGM〜

2月16日(月)

昨日から10時間以上TMPに悩まされ続けた。朝のハッキリ脳で進展はあったが、確実な論理まではたどり着けず。実用でOKなラインで妥協することにする。とりあえず、忘れる前にメモ。

ダミーフォント作成時の重要ポイント

ダミーFontAssetは元フォントの影響を受けるので、FaceInfoを必ず整える。

ビットマップ用途では:

  • Scale = 1
  • Ascent = 14
  • LineHeight = 14
  • Baseline = 0
  • Descent = 0

👉 数値は何でもいい14としたが其々10にしても問題ない。

TMPのFontSizeについて

理論的には:表示サイズ ≒ DM.FontSize × (BF.Ascent / BF.PointSize)

DMはダミーのフォント、BFはビットマップから生成したビットマップフォント。判別しやすいようにオブジェクト風にXX.と記述しただけ。

実際は上記の論理だとだめで、大きめに表示されてしまう。

自分の環境で、ビットマップフォントの1pxを他のスプライトの1pxに合わせるには、

TMPのフォントサイズを8にする

とほぼ近かった。ということで論理はわからんがOKとする!!!!😢

2月17日(火)

DTDの大幅リニューアルに入ることになった。全体をドット絵調の画面やスケールで統一することにした。いままで、なんとなくで作り始めてたからね😰

とりあえず、メインのStageSceneから始める。この画面のレイヤー構成としては

  • タイピング画面
  • UI
    • リザルト画面
    • BGM情報
  • (エフェクト)
  • タワーディフェンス画面
    • 敵城、自城、キャラクター、地面
  • 背景

といったこころだ。地面に関しては透明な仮想的なものにしようと思う。地面は背景に描画しておき、地面はあくまでもキャラクターが動き回る透明な板という扱いだ。

ここで画面設計をする上で重要な項目を確認しておく。

  • ゲーム世界の画面解像度:640×352
  • ゲーム世界のアスペクト比:20:11(16:9に近い比率でありながら16の倍数)
  • PixcelPerUnit:16
  • マップチップの個数:20×11

画面のSortingLayer 構成(奥→手前)とOrderは

  • Default(最奥)
  • Background
  • Effect_Back
  • Unit
    • Castle Order: 0(奥)
    • Unit Order: 10(手前)
  • Effect_Front
  • UI
3D View – Unity

画面構成をしっかりメモした。

2月18日(水)

風っぽい

2月19(木)

ダウン

2月20日(金)

回復!ハイエースの当選報告はまだかあぁああああああああ!

タイピングゲームは2種類にするのもありかも

  • 毎パソライクモード
    • キャラは変更不可
    • 挑戦スコアに応じて敵の出現度合いをコントロール目標達成すれば城まで到達
    • ノーミスが続いたら(ひかえめ)演出で気分が上がるようにしてみては?
  • DTDモード
    • デッキ変遷ができて、キャラを切り替えて攻略する

2月21日(土)

ドット絵調ゲームのベスト解像度は?に設定方法など詳しいことを追記した。疲れた😩

2月22日(日)

画面解像度の変更をきっかけとした、大規模な作り直し作業を進めてきたところ、ボタンのところまでたどり着いた。以前の開発日記でUniversalButtonという背伸びをして作ってしまった複雑怪奇なシステム🤪を単純化することにしたわけだが、方針は決めたものの、単純化の作業は行っていなかったので、ここで付けを払うこととなった🤮

複雑怪奇システムUniversalButtonについて

  • UniversalButtonというプレハブ
  • UniversalButton.csがアタッチされている
  • Inspector上でボタンアクションをアサインできるようになっている
  • ボタンアクションはScriptableObjectとして定義されており、ボタン固有の振る舞いを実装したスクリプトとセットで動作する設計になっている。

このシステムの難点は、ボタンごとの振る舞いスクリプトが、アクションの数だけ増えていくことだ。同時に、それに対応するScriptableObjectアセットも増えてしまう。一元管理されているため、共通処理の再利用や仕様変更時の修正コスト削減には貢献するが、DTDにおいてはボタン動作の再利用性はそれほど高くなく、結果として抽象化のレイヤーが過剰になり、システムが大きすぎて扱いづらいものになっていた。

新システムについて

  • ベーシックなButtonコンポーネントのラッパー
  • ボタンの動作は各シーンのXXXManagerにOnClick_XXXXXXで記述

新システムなどと銘打っているが、やっていることはベーシックなButtonコンポーネントの使い方への回帰に過ぎない。ただし、直接Buttonを使うのではなく、簡単なラッパーコンポーネントを用意し、連打防止などの制御を追加している。

動作の設定方法はButtonコンポーネントと同様で、シーン上のオブジェクトをインスペクターにアサインし、そこから実行したいメソッドを選択する形にしている。

DTDでは各シーンにXXXManagerという管理用オブジェクトを必ず配置する方針にしているため、このオブジェクト側にOnClick_XXXXXXという命名規則でボタン用メソッドを用意することにした。

また、ログアウトのように複数シーンから呼び出される可能性のある処理については、シーン固有のManagerではなくGameManagerに実装したくなる。しかし、GameManagerはログイン時に生成され、DontDestroyOnLoadでシーンをまたいで保持される実行時オブジェクトであるため、各シーンのエディタ上では直接参照できず、OnClickイベントとしてInspectorに登録することが出来ない。

つまり、ボタン動作はすべてXXXManagerに記述する!という結論に至った。

2月23日(月)

2月24日(火)

ハイエースはずれたああああああ!!!

コメントする