はじめてのGodot 第2回: 宇宙船をキーボードで動かそう — はじめてのGDScript


前回は宇宙船を画面に表示しました。今回はプログラムを書いて、矢印キーで動かします。プログラミングが初めての人にとって、連載で一番大きな一歩の回です。ゆっくり進めましょう。

今回のゴール

矢印キーで宇宙船が上下左右と斜めの8方向に動く様子

矢印キーで宇宙船が上下左右(と斜め)に動く。

スクリプトをアタッチする

Godotでは、ノードに「スクリプト」というプログラムを取り付けて動きを与えます。

  1. シーンドックで Player を選択
  2. 右クリック→「スクリプトをアタッチ」
  3. 設定はそのまま「作成」(player.gd というファイルができます)

画面中央がコードエディタに切り替わり、こんな内容が表示されます。これは、上中央のワークスペースが自動で「スクリプト」に切り替わったためです(第1回で紹介したあのタブです)。部品の配置に戻りたくなったら、上中央の「2D」を押せばさっきの画面に戻れます。今回は「スクリプト」のまま進めます。

extends Sprite2D

これは「このプログラムはSprite2D(画像表示ノード)の機能を引き継いで、さらに付け足す」という宣言です。今は深く考えず「1行目はお約束」と思ってOKです。

動かすコードを書く

次のように書き換えてください(コピペでOKですが、一度手で打つとエラー体験ができておすすめです。理由は後述)。

extends Sprite2D

var speed = 400

func _process(delta):
	var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	position += direction * speed * delta

保存(Ctrl+S / MacはCmd+S)して F5(MacはCmd+B)で実行してみてください。矢印キーで宇宙船が動いたら成功です。

何が書いてあるのか、1行ずつ

var speed = 400 — 変数

var は「変数」を作る命令です。変数は名前付きの入れ物で、ここでは speed(速さ)という名前の入れ物に 400 を入れています。

なぜ直接 400 と書かずに変数にするのか? 後から変えやすいからです。試しに 4001000 にして実行してみてください。爆速になります。100 ならノロノロです。気持ちいい速さを探してみてください。これがゲーム開発の「調整」という作業で、変数はそのための仕組みです。

func _process(delta): — 毎フレーム呼ばれる関数

ゲームは、パラパラ漫画のように1秒間に60回ほど画面を描き直しています。この1回1回を「フレーム」と呼びます。

_process は特別な関数で、Godotが毎フレーム自動で呼び出してくれます。つまりここに書いたことは1秒に60回実行されます。「ゲームの心臓部」だと思ってください。

Input.get_vector(...) — キー入力を方向に変換

Input.get_vector() は、4つのキーの押され具合を「方向」としてまとめてくれる便利な命令です。"ui_left" などはGodotにあらかじめ用意されている入力名で、矢印キーに対応しています。

右を押せば (1, 0)、上なら (0, -1)、右上なら斜めの方向が direction に入ります。

position += direction * speed * delta — 実際に動かす

position はノードの現在位置です(前回インスペクタでいじったアレです)。そこに「方向 × 速さ × delta」を毎フレーム足し込むことで移動します。

delta を掛けるのはなぜ? delta には「前のフレームからの経過時間(秒)」が入っています。これを掛けておくと、60フレーム/秒のPCでも30フレーム/秒の古いPCでも、1秒あたりの移動距離が同じになります。掛け忘れると、性能のいいPCほどゲームが速くなる昔のゲームのようなバグになります。「動く処理にはdeltaを掛ける」と覚えてください。

わざとエラーを出してみよう

ここで大事な練習をします。position の行の先頭にあるTab(字下げ)を消して、保存してみてください。

Parse Error: Unexpected identifier "position" in class body.

という赤いエラーが出ます。GDScriptでは字下げ(インデント)が「その行が関数の中身かどうか」を決めます。Tabを消すと position の行が関数の外(クラスの直下 = class body)に押し出され、「こんな場所に position があるのはおかしい」と怒られるのです。Tabを打ち直せば直ります。

エラーは敵ではありません。「どの行の・何が・どうおかしいか」を教えてくれる案内板です。この連載ではこれから何度もエラーに会いますが、毎回「行番号→メッセージ」の順に読めば必ず直せます。

つまずきポイント

  • Identifier "postion" not found: スペルミスです。エラーメッセージに間違った綴りがそのまま表示されるので、コードと見比べてください(position の打ち間違いが定番)
  • 保存したのに反映されない: 実行中のゲームは古いままです。一度閉じて F5(MacはCmd+B)し直してください
  • 動きがカクカクする: delta の掛け忘れがないか確認してください

今回学んだこと

  • スクリプトはノードに取り付けるプログラム
  • var で変数(名前付きの入れ物)を作る。数値を変えれば挙動が変わる
  • _process(delta) は毎フレーム呼ばれるゲームの心臓部
  • 動く処理には delta を掛けてフレームレートの影響をなくす
  • エラーは「行番号→メッセージ」の順に読む案内板

次回予告

今の宇宙船は画面の外まで飛んでいけてしまいます。第3回では画面の中に閉じ込める処理を書きながら、「関数を自分で作る」ことを学びます。

第3回: 画面の外に出ないようにしよう — 関数とclamp