🎮

Godotを触ってみた話

に公開

https://adventar.org/calendars/12375

この記事はteam411 Advent Calendar 2025の25日目の記事です。

昨日の記事はたびと!/Tachyonさんの「GAS(Google App Script)で生徒と教師をつなぐ論理回路学習教材を作った話」でした。
https://zenn.dev/tabito1419/articles/327f0769b66fd3
授業のためにWebアプリを作るなんてすごいですね!
私の時の情報は共通テストで使わないのもあってかなり適当だったので、私もあんな授業受けてみたかった、、、
GASの使い方も面白くてめちゃくちゃ参考になったのでまだ読んでない人はぜひ読んでくださいね!!

はじめに

皆さんこんにちは!team411という学生団体に所属しているelmoです。

いよいよ今年のアドベントカレンダーも最終日ですね。
もちろんこの記事を読んでいる皆さんは家で一人で読んでくれていることだと思います。私もいまこの記事をバイト終わりに投稿しています。

クリスマス?なんですかそれ(血涙)

実は初日にも記事を書いているのですが、あの記事は企画担当者兼サークルの広報担当としての記事でしたので、本記事では技術っぽい話をしたいと思っています。
初日の記事が気になる人は下のリンクから読んでやってください。
(自己紹介とかもこっちでやってるので本記事では割愛します。)

https://zenn.dev/team411/articles/819847edcc54fb

本記事で話すこと

本記事では最近触って良かったなと思えた Godot Engine 4 についてしゃべりたいと思っています。

あくまで私の作成記録なので、ベストプラクティスや新規性の高い成果物は出てきませんので、ご容赦下さい。技術半分、布教半分ぐらいで読んでくださるとありがたいです。
また記事の前半は経緯やGodotの説明が占めるので、一刻も早く技術の話が知りたいへn..、ベテランの方はここから読み始めてください。


Godot Engineって何?

さて早速皆さんにGodotについて紹介します。
Godot Engine (ゴドー エンジン)はUnityやUnreal Engineに並ぶゲームエンジンです。

  • 2Dのゲーム製作が得意
  • Unityなどと違ってオープンソースのソフトウェア
  • 無料

私が何よりいいと思ったのは非常に軽いことで、プロジェクトを開くのに5秒もかかりません。
以前Unityで開発をする機会があったのですが、その時プロジェクトを開いて、様々な要素を読み込むのに1~2分かかっていたことを考えると、非常に早く感じます。

対応言語はオリジナルのGDScriptかC、C++、C#です。GDScriptはPythonに似た言語で、Python以上に変数の厳格な記法を書くことが可能です。Godotはシーンという入れ子構造によって構成される独特のゲームデザインアーキテクチャになっていて、GDScriptはこれに最適化された言語になっています。

Godotを知った経緯・選んだ理由

この章は完全に自分語りですので、読みとばしていただいて構いません。

最近私はあるクリエイターの動画にはまっていました。
その名も遷移圏見聞録。主にAIやBlenderを用いて実写とCGをまぜたような動画を作成して、異世界情緒な世界観の動画を投稿されています。

https://www.youtube.com/@seni.kenbun
https://senikenbun.wixsite.com/home

クリエイターさん自体のことを知ったのは半年ぐらい前のことだったのですが、中間テストなど諸々のストレスにさらされていた私は好きなことをやりたい欲に捉われて、この世界観の2次創作を作りたくなりました。

さて一般に2次創作というと絵を描いたり、小説を書くことが思い浮かびますが、私には画才も文才もありません。(作文はいつも文字稼ぎばかりだったし、美術の成績は定期試験の筆記で何とか保っていました。)
そこで私は簡単なミニゲームを作ろうと思いつきました。ゲーム自体のギミック部分をコーディングで書いて、ゲーム内で使用するキャラクター画像などはいい感じにAIを使えば作れると思ったからです。
と言っても、私はWebサイト制作ぐらいしかやったことがなく、ゲーム製作は少しかじった程度だったのでまずは簡単に動くものを作ってゲーム製作の一連の流れを体験してみようと思いました。

始めは別プロジェクトで触ったことのあるUnityを使おうと思っていましたが、とある理由で断念しました。それはUnityというゲームエンジンの特性が関係しています。そもそも最初から3Dゲームを作ることは無謀だと感じていたので、2Dゲームを作成しようと思っていました。しかしUnityは本質的に3Dエンジンであり、2D機能は3D空間のZ軸を無視して板ポリゴンを表示することで実現されています。つまり本来2Dゲームには余計な機能が多すぎて混乱すると思いました。

よって2Dエンジンを専用で持っていて、Unityに劣らない魅力を持つゲームエンジンを探していたところ、Godotを見つけました。
実際、UnityやUnreal Engineでは、浮動小数点演算の誤差により、ピクセルが滲んだり、キャラクターの配置が微妙にズレたりする現象(Sub-pixel renderingの問題)に対処するために、複雑なカメラ設定やアセットの調整が必要となる場合がありました。 一方、Godotはピクセル単位の座標系をネイティブにサポートしており、デフォルトの設定で「ピクセルパーフェクト(ドットの輪郭を鮮明に保つ描画)」を実現しやすい利点があります。

他にも利点を挙げればきりがないので、気になった人は詳しく調べてみてください。(丸投げ)

ここから制作の話

さてようやく、作ったものの話です。ここで一度見苦しい言い訳をするのですが、本記事はアドカレ記事作成の5日前に思いついたので、作ったものはかなり簡素なものになっています。なんだこんなものかと思っても、許してください。

成果物の概要

今回作ったゲームは「ローグライク風RPGゲーム」です。

主な機能は以下の通りです。

  • グリッドベース移動(1マスずつ動く)
  • 敵との衝突・ゲームオーバー
  • アイテム収集とUI表示
  • シーン遷移(ゴール)

アセットはKenney (Micro Roguelike / Colored Packed)を使用しました。また、zennはgifしか貼れなかったのですが、BGM、コインを取る音、敵にぶつかって踏みつぶされる音も実装しています。

技術的なトピック

今回の制作期間は1日でしたが、単にキャラクターを動かすだけでなく、「RPGっぽさ」を出すためにいくつかのGodot特有の機能を活用しました。
特に勉強になったポイントを2つ紹介します。

1. RayCastとTweenで作る「グリッド移動」

今回一番こだわったのが移動システムです。
アクションゲームなら position += velocity で滑らかに移動させれば良いのですが、ローグライクなどのRPGは「1入力で1マス進む」「マスの途中で止まらない」という挙動が必要です。

これを実現するために、以下の2つを組み合わせました。

  • RayCast2D(棒状の当たり判定っぽいもの)
  • Tween(補間アニメーション)
  1. 入力があった方向へ RayCast2D を向ける。
  2. 「目の前が壁じゃないか?」をチェックする。
  3. 壁がなければ、Tween を使って隣のマスまでアニメーション移動させる。

実際のコード(抜粋)はこんな感じです。

player.gd
# CharacterBody2D: 物理演算による移動や衝突判定を行うキャラクター用ノード
extends CharacterBody2D

# 1マスのサイズ(8px)
const TILE_SIZE = 8
# 移動にかかる時間
const MOVE_SPEED = 0.2

@onready var ray = $RayCast2D

func move(dir):
    # 1. RayCastを行きたい方向に向ける
    # 1.5倍にしているのは、壁の表面ではなく中心まで届かせて確実に判定するため
    ray.target_position = dir * TILE_SIZE * 1.5
    
    # 【重要】物理演算の更新を待たずに、今すぐ判定を更新する
    ray.force_raycast_update()
    
    # 2. Rayが壁にぶつかっていなければ移動開始
    if not ray.is_colliding():
        start_tween(dir)

func start_tween(dir):
    var tween = create_tween()
    # 現在地から1マス先まで、0.2秒かけて移動
    tween.tween_property(self, "position", position + dir * TILE_SIZE, MOVE_SPEED)

この実装で面白かったのは、force_raycast_update() という命令です。通常、物理判定は次のフレームで更新されるのですが、これを書かないと「RayCastを向けた瞬間」の判定が取れず、壁にめり込んでしまうことがありました。
また、Tweenのアニメーション中にY座標を少し上下させることで、ドラクエのような「ピョコピョコ歩く」動き(Juice/手触り感)も簡単に実装できました。

2. シグナルを使った「疎結合」な設計

Godotには Signal(シグナル) という、Observerパターンを簡単に実装できる機能があります。
これを活用することで、「プレイヤー」と「UI」のコードを完全に切り離すことができました。

例えば「コインを拾ったとき」の処理です。

  • ❌ ダメな例: プレイヤーのコードの中に UI.text = "コイン: 1" と直接書き込む(依存関係が強すぎる)。
  • ⭕ Godot流: プレイヤーは「拾ったよ!」と叫ぶだけ。UI側がそれを聞きつけて勝手に更新する。

Player.gd (叫ぶ側)

# シグナルを定義
signal coin_changed(num)

func collect_item():
    coins += 1
    # 「コイン枚数が変わったよ!」と発信(emit)するだけ
    coin_changed.emit(coins)

UI.gd (聞く側)

# エディタ上で接続しておくと、この関数が自動で呼ばれる
func _on_player_coin_changed(num):
    text = "Coin: " + str(num)

これのおかげで、もしUIを削除したり変更したりしても、プレイヤー側のコードを一行も書き直さなくて済みました。

開発中に出たエラーと解決策

「Godotは初心者にも優しい」と言われていますが、それでもいくつか頭を抱えたポイントがありました。
これからGodotを触る人のために、私が踏み抜いた地雷を共有しておきます。

地雷1:親を小さくすると、子供も小さくなる(RayCast届かない問題)

今回のアセットは8x8ピクセルと非常に小さかったため、最初はプレイヤーの親ノード(CharacterBody2D)の Scale を 0.1 などにして調整しようとしていました。
すると、子ノードとして持たせていた RayCast2D(当たり判定)まで10分の1サイズになってしまったのです。

コード上では「12ピクセル先を調べろ」と書いているのに、実際には「1.2ピクセル」しか伸びておらず、壁に密着しないと反応しない……というバグに数時間悩まされました。

解決策
親ノード(ルート)のScaleは 1 のままにしておき、見た目担当の Sprite2D (※テクスチャなどの画像を表示するためのノード) だけを縮小(または拡大)するのが鉄則でした。

地雷2:物理演算中に世界を消してはいけない

階段(ゴール)に乗ったらシーンを切り替える処理を作ったときのことです。
衝突検知の _on_body_entered 関数の中で change_scene_to_file を呼んだところ、こんなエラーが出てゲームが落ちました。

Removing a CollisionObject node during a physics callback is not allowed...

要するに「物理演算の計算中に、計算対象(自分やプレイヤー)を消したり移動させたりするな!」と怒られたわけです。

解決策
call_deferred(予約実行)を使います。「今の計算が終わったら、あとでやっておいてね」とGodotにお願いする書き方です。

func _on_body_entered(body):
    if body.name == "Player":
        # × 直接呼ぶとエラーになる
        # get_tree().change_scene_to_file("res://win_screen.tscn")
        
        # ○ 予約実行なら安全
        get_tree().call_deferred("change_scene_to_file", "res://win_screen.tscn")

地雷3:引数のアンダースコア _ の罠

GDScriptでは、関数内で使わない引数には _ を付ける(例: _delta)と警告が消えるという仕様があります。
これを鵜呑みにして、シグナル接続された関数の引数名を _area に変えたのですが……。

# 引数名を _area に変えた
func _on_hitbox_area_entered(_area):
    # 中身で area を使おうとしてエラー
    if area.is_in_group("enemies"): 
        pass

後から処理を追加した際に、引数名の _ を取り忘れていて「変数 area が見つかりません」という初歩的なエラーを出してしまいました。
警告消しは便利ですが、後からコードをいじる時は注意が必要です。

感想

本来は、411でやってるプロジェクトでデプロイした話を書こうと思っていたのですが、やってみるとあまりに作業過ぎて、特段苦労もなかったので急遽テーマを変更しました。
Unityで挫折しかけた経験がある私ですが、Godotは「ノードを組み合わせてシーンを作る」という考え方が非常に直感的で、プログラミング初心者や個人開発者には特に相性が良いツールだと感じました。 何より、プロジェクトが爆速で開くので「ちょっと30分だけ進めようかな」という気になれるのが最高です。

また、記事を書いていて思うのは、実際にいろいろ調べながらやった方が記事書くの早いなということです。この記事を書いていて改めて使った技術の再確認ができてよかったです。
動機のところでも書いた2次創作ゲームは今年度中を目途に作ろうと思っていますので、面白そうだったらまた記事にします。
何よりこの記事を読んで「自分も作れそうかも?」と思った方は、ぜひ年末年始のお休みにGodotを触ってみてください!

最後に

team411アドベントカレンダー2025、これにて終了となります。
エンジニアのアドカレということで遅れが発生するのが常であるようですが、今年は全員が期限ぴったりに提出してくださり本当に感謝しています。慣れない運営でしたがここまで走れて本当に良かった。

記事を書いてくれた411のメンバー、毎日ツイートとHP編集をレビューしてくれた役員のみんな、
何より、1ヶ月記事を読んでくださった読者の皆さん。本当にありがとうございました!
まだ全部読めてないという人もそれぞれの記事が良く書かれているのでぜひ読んでみてください!

また、これらの記事を読んでteam411に興味がわいた方は、ぜひ下のホームページをご覧になってください。来期の入部に関することも載っています!
https://team411.net/

参考文献

https://www.reddit.com/r/gamedev/comments/1fxd33a/unity_vs_godot_pros_and_cons_of_each_which_is/
https://godotengine.org/ja/
https://kenney.nl
https://forum.godotengine.org/

Discussion