跳到正文
zenn · 游戏开发社区· 来源· Cz_mirror·· 4 小时前AI 评分55

从自制游戏中提取出日语输入组件,作为 Godot 4 用的 addon 公开了

自作ゲームから日本語タイピング部品を切り出して、Godot 4用のaddonとして公開しました

正文 · 原文

はじめに

自作のタイピングガンアクション「TypeGunner」(Godot 4)で使っていた、日本語をローマ字で打つための入力部品を、別のゲームでも使えるように切り出して公開しました。

  • Godot 4.6系、typed GDScript
  • MIT License、v0.1.0
  • 外部依存なし

この記事では、何ができるのかを簡単に紹介したあと、ゲームから切り離すときに考えた設計の話と、切り出しの手順を書きます。導入方法だけ知りたい場合は、READMEの「導入」を見てください。

何ができるか

ゲーム側は「タイプ対象のNodeに、かなを添えて登録する」だけです。

TypingEngine.register_target(enemy, {"kanji": "日本語", "kana": "にほんご"})

あとはプレイヤーがキーを打つと、addonがローマ字の候補と照らし合わせ、1文字ごと・単語を打ち終えたときにsignalで知らせてくれます。

ローマ字は1通りではなく、よくある打ち方をまとめて受け付けます。たとえば次のような入力がどれも正解になります。

かな 受け付ける打ち方(一部)
し shi / si / ci
ち chi / ti
ふ fu / hu
しゃ sha / sya / shixya(分解して打つ)
きって kitte / kixtsute / kiltute など
かんじ kanji / kannji / kaxnji
ティー thi- / texi-

カタカナはひらがなとして扱うので、コーヒー のような語もそのまま登録できます。

ほかに、次の部品が入っています。

  • TypingDisplay.tscn: かなと、入力済み・次の1文字・残りを色分けしたローマ字を表示する部品
  • KeyboardDisplay.gd: JIS/US配列のキーボードを描き、担当指の色と次のキーを示す練習用の部品
  • WordPicker.gd: 語彙から出題語を選ぶstatic関数

なぜ切り出したか

次に作るゲームでも日本語タイピングを使いたいと考えたのがきっかけです。コピーして使うこともできますが、ローマ字の打ち方の修正(「ん」の扱いなど)を両方に入れる手間を考えると、1か所で直して両方に配るほうが楽です。

そこで「1 addon = 1 リポジトリ」とし、ゲーム側は addons/typing_core にgit submoduleで取り込む形にしました。

設計で決めたこと

依存は「ゲーム → addon」の一方向だけ

切り出すにあたって、addonが知っていてよいことを次の範囲に絞りました。

  • どのNodeが、どのかなを打たせる対象か
  • いま何文字目まで打てているか、どの対象をfocusしているか

ダメージやスコア、撃破演出は全部ゲーム側の仕事です。addonはsignalを出すだけで、受け取ってどうするかには関与しません。

signal word_completed(unit: Node)
signal char_typed(unit: Node)
signal mistype(unit: Node)
signal no_target_hit
signal focus_changed(unit: Node)

対象の寿命はNodeに任せる

タイプ対象はシーンツリー上のNodeそのものです。登録時に tree_exiting へ接続しておき、Nodeが消えたら自動で登録を外します。

var exit_callable: Callable = _on_target_tree_exiting.bind(unit)
if not unit.tree_exiting.is_connected(exit_callable):
	unit.tree_exiting.connect(exit_callable, CONNECT_ONE_SHOT)

おかげでゲーム側は、敵を倒したら queue_free() するだけでよく、解除のし忘れで「画面にいない敵が打てる」ことが起きにくくなりました。なお、ツリーに入れていないNodeには tree_exiting が来ないので、その場合だけ自分で unregister_target() します。

「最初の1文字」でfocusする

画面に複数の対象があるとき、何もfocusしていない状態で打った1文字目が候補に合う対象のうち、先に登録したものをfocusします。以降は、TabかEscでfocusを外すまで、その対象だけに入力が入ります。

この規則だと、先頭の文字が同じ語が並んだとき、後から出た語を選べません。そこで出題側の WordPicker.pick() には、画面上の語の先頭文字を除外する引数を持たせました。条件で候補が空になったら先頭文字の条件だけを外し、それでも空なら全語から選ぶ、という順で必ず1語を返します。

ローマ字変換は状態を持たない部品に分けた

当初は入力判定とローマ字の候補生成が1つのAutoloadに入っていましたが、公開前に候補生成を RomajiConverter.gd に分けました。かな文字列を受け取って、入力単位ごとの候補を返すだけの、状態を持たない部品です。

# 「かんじ」を区切った結果
# か ["ka", "ca"] / ん ["nn", "xn", "n"] / じ ["ji", "zi", "nji", "nzi"]

変換表は kana_romaji.json(かな → ローマ字の配列)に外出ししてあり、打ち方を足したいときはJSONを編集すれば済みます。

ただ「っ」と「ん」だけは、表ではなくコードで決めています。どちらも前後のかなで正解が変わるからです。

  • 「っ」: 次のかなの子音を重ねる入力(kitte)を受け付ける。末尾の「っ」は xtsu などの単独入力だけ
  • 「ん」: 次のかなが母音・や行・な行で始まるときは、n 1つだと次のかなと区別できないので nn / xn を求める(「きんえん」の「ん」は nn か xn)。末尾の「ん」は n だけ
  • 「ん」を n 1つで打てる位置では、続けてもう1つ n を打つ入力(kannji)も受け付ける

状態を持たないので、Autoloadを立てずにこの部品だけ試験できます。こうした境界ケースは tests/run_romaji_converter_tests.gd に固定しました。

表示は打った綴りに追従する

TypingDisplay は、入力済みの文字に、残りの区切りの先頭候補をつなげて表示します。「にほんご」を nihonn まで打つと、表示は nihonn + go に変わり、プレイヤーが選んだ綴りに合わせて次の1文字が示されます。

候補の並び順は表示に直結するので、たとえば「ん」の後ろに n を足した候補は元の候補より後ろに置いています。先に置くと「せんせい」が sennnsei と表示されてしまうためです。

置き場所に依存しない読み込み

addonはどこに置かれるかわからないので、JSONや他のscriptは、自分のscriptと同じフォルダから読みます。

var base_dir: String = get_script().resource_path.get_base_dir()
var converter_script: GDScript = load(base_dir.path_join(ROMAJI_CONVERTER_FILE)) as GDScript

また class_name と preload は使っていません。class_name を使わないので、取り込み先のゲームのグローバルなクラス名と衝突しません。

addon単体で試験できるようにした

tests/run_tests.sh は、一時フォルダに最小のGodotプロジェクトを作ってaddonを複製し、TypingEngine をAutoload登録してから試験を走らせます。ゲームに取り込まなくても、addonのリポジトリだけで回帰試験ができます。

GODOT=/path/to/godot sh tests/run_tests.sh

切り出しの手順

いきなり別リポジトリに移すのではなく、段階を踏みました。

  1. TypeGunnerの中で、タイピング関連のファイルを addons/typing_core/ に集め、ゲーム固有のAutoloadへの参照をなくす
  2. 3か所に散っていた出題語の抽選を WordPicker.gd にまとめる(旧実装と3000回比べて結果の一致を確認)
  3. git subtree split でそのフォルダの履歴ごと新しいリポジトリへ移す
  4. TypeGunner側の addons/typing_core をsubmoduleに置き換える
  5. 公開前の整理として、ローマ字変換の分割、キー配列のJSON化、カタカナと外来語のかな(「てぃ」「ゔぁ」など)の追加、MIT Licenseと plugin.cfg の追加を行う

1の段階でゲームの中のまま依存を断っておいたので、3と4はファイルを移すだけで済みました。移した後もTypeGunnerで動作を確認しています。

制限

v0.1.0の時点で、次のことはできません。

  • 漢字の入力。kana に変換表にない文字を含めると、その語は打てません(登録時に警告が出ます)
  • 記号や数字の入力。受け付けるキーは a〜z と -(長音)だけです
  • WordPicker に渡す語彙の romaji は、自分で書く必要があります。先頭文字と長さの判定に使います

おわりに

日本語タイピングのゲームを作るとき、ローマ字の打ち方の揺れや「ん」「っ」の扱いは地味に手間がかかるところです。Godotで同じようなゲームを作る方の役に立てばうれしいです。導入手順やAPIはREADMEにまとめています。

来源:zenn · 游戏开发社区 · zenn.dev