[{"content":" はじめてのGodot Godotゲームエンジンの入門ガイドです。これまでゲームエンジンを使ったことがない方、あるいはGodotを初めて使う方にとって、最適なスタート地点となるでしょう。\n目次 ： はじめに GDScript入門 3D入門講座 関連項目 ゲームチュートリアル／初めての2Dゲーム開発 ","description":"","tags":null,"title":"はじめてのGodot","uri":"/godot_recipes/4.x/ja/g101/index.html"},{"content":" 3Dアセットを扱う モデル、アニメーション、マテリアルを含むインポートおよび編集方法を解説。\n以下の3Dアセットを使用します。Kay Lousberg提供のものです。\n冒険者キャラクターパック ダンジョンアセットパック 目次 ： アセットのインポート キャラクターアニメーション キャラクタコントローラー ","description":"","tags":null,"title":"3Dアセットの扱い方","uri":"/godot_recipes/4.x/ja/3d/assets/index.html"},{"content":"本チュートリアルでは、以下のGodotで3D制作を始める方法について解説します。\n3Dエディタ内での移動方法 3Dオブジェクトの作成と操作方法 カメラやライティングなど、Godotの基本的な3Dノードの使い方 準備はOK？ 注意点として、3D開発は2D制作と比べて格段に複雑になる場合があります。ノードの操作やスクリプト記述、ロジック／データ管理など、多くの基本原則は同じですが、3Dではその他にも考慮すべき要素が数多くあります。そのため、最初の数プロジェクトは2Dで進めることをオススメし、ゲーム開発プロセスをしっかり理解した上で3Dに移行すると良いでしょう。このチュートリアルでは、公式Godotチュートリアルにあるような初心者向けGodot 2Dプロジェクトを既に作ったことがあることを前提としています。\n3D制作を始めるための基本ガイド Godotの大きな強みの一つは、2Dゲームと3Dゲームの両方を開発できる点です。これまで2Dプロジェクトで習得した知識（ノード、シーン、シグナルなど）の多くは3D環境でも同様に適用できますが、同時に全く新しいレベルの複雑さと機能も追加されています。まず初めに、3Dエディターウィンドウで利用できる新機能をご紹介します。\n3D空間での方向把握 Godotで新しいプロジェクトを初めて開くと、3Dプロジェクトビューが表示されます。\nまず最初に注目すべきは、中心に配置された3本の色分け軸です。 これらはx軸（赤色）、y軸（緑色）、z軸（青色）を表しています。これらが交わる点を 原点 と呼び、座標値は(0, 0, 0)です。なお、この配色は他のインスペクタ画面でも一貫したルールで適用されていることに気づくでしょう。\nメモ 異なる3Dアプリケーションでは、座標軸の向きに関してそれぞれ独自の規約を採用しています。 GodotではY軸上方方式を採用しているため、座標軸を見た場合、x軸が左右方向を指すとき、y軸は上下方向を、z軸は前後方向を示します。 他の多くの人気3DソフトウェアではZ軸上方方式が使われています。アプリケーション間を行き来する際には、この違いを把握しておくことが重要です。\n3D空間での移動操作は、マウスとキーボードを使用して行います。以下に、ビューカメラの基本コントロールについて説明します。\nマウスホイール上下：拡大／縮小表示・縮小表示切り替え 中ボタン＋ドラッグ操作_：現在のターゲットを中心にカメラを周回移動 Shift + 中ボタン＋ドラッグ操作：カメラ視点を平行移動 右クリック＋ドラッグ操作：その場でカメラ向きを回転 さらに、もし人気のある3Dゲームに慣れているなら、 フリールックモード が適しているかもしれません。これは Shift+F キーで切り替えできます。このモードでは以下の操作ができます。\nWASDキーを使ってシーン内を自由に移動しながら視点を変更 マウスを使ってカメラの狙いを調整 また、左上隅の [透視投影] をクリックすることで特定の向きにカメラ視点を変更できます。\n3Dオブジェクトの追加方法 では、最初の3Dノードを追加してください。2DノードがすべてNode2Dを継承しているのと同様に、 positionやrotationなどのプロパティを提供しているように、3DノードはNode3Dを継承しており、同じプロパティの3Dバージョンを提供しています。シーンに1つ追加すると、原点位置に以下のオブジェクトが表示されるでしょう。\nこのオブジェクトはノードではありません。3D ギズモ と呼ばれるものです。ギズモとは、空間内でオブジェクトを移動・回転させるためのツールです。3つのリングで回転操作を行い、3本の矢印でオブジェクトを各軸方向に平行移動させます。なお、リングと矢印はそれぞれ対応する軸の色に合わせて配色されています。\n数分間かけて操作に慣れ、機能の使い方に慣れてください。もし迷ってしまったら、「元に戻す」機能を使ってみてください。\nヒント ギズモを煩わしく感じる場合は、モードアイコンをクリックすれば、「移動」「回転」「スケール(拡大縮小)」に限定できます。 グローバル空間とローカル空間の比較 デフォルトでは、ギズモ制御はグローバル座標空間で動作します。オブジェクトを回転させると、ギズモの矢印は引き続き軸方向に並びます。ただし、ローカル空間を使用 ボタンをクリックすると、ギズモはボディの動きをローカル座標空間に切り替えます。\nオブジェクトを回転させると、そのギズモの矢印は オブジェクト の座標軸に沿って表示されます。ワールド座標系とローカル座標系を切り替えながら操作することで、より正確に狙った位置に配置できるようになります。\nトランスフォーム ノードのインスペクターを確認してください。「トランスフォーム」セクションには、位置、回転、スケールに関するプロパティが表示されます。ギズモを使ってオブジェクトを移動させながら、これらの値が変化していく様子を観察してください。2Dと同様、これらのプロパティはノードの親要素に対する相対値として扱われます。\nこれらのプロパティが組み合わさることで、ノードの transform （座標変換）が定義されます。コード内でノードの空間的特性を変更する際には、transformプロパティを操作します。このプロパティはGodotが提供するTransform3Dオブジェクトです。これにはoriginとbasis2つの主要な属性があります。originはオブジェクトの位置ベクトルを示し、basisにはオブジェクトのローカル座標軸を定義する3つのベクトルが含まれます。これは ローカル空間 モード時にギズモに表示される3本の座標軸矢印に相当します。\nこのセクションの後半で、これらのプロパティの使い方を説明します。\nメッシュ Node2Dと同様に、Node3Dにも独自のサイズや外観はありません。2D空間では、Sprite2Dを使用してノードにテクスチャを追加するのが一般的です。一方3D環境では、代わりに メッシュ を追加する必要があります。\nメッシュとは、形状を数学的に表現したものです。これは一連の点（頂点）で構成されており、これらの頂点は線分（辺）で接続されます。複数の辺がつながることで（最低3つ以上）、最終的な立体面である ポリゴン が形成されます。\nたとえば、キューブは8つの頂点、12本の辺、6つの面で構成されています。\nメッシュの追加方法 通常、メッシュはBlenderなどの3Dモデリングソフトウェアを使用して作成します。 自分で作成できない場合には、ダウンロード可能な3Dモデルのコレクションも多数利用できます。 ただし、多くの場合必要なのは単なるキューブや球といった基本的な形状だけです。このような場合、Godotでは プリミティブ と呼ばれるシンプルなメッシュを作成する機能を提供しています。\nMeshInstance3Dノードを Node3Dの子として追加し、インスペクターでその メッシュ プロパティをクリックします。\nここで利用可能なプリミティブのリストを確認できます。これらは便利な機能を集約した、一般的な有用形状のコレクションです。新規から「BoxMesh」を選択すると、 画面上にシンプルなキューブが表示されるはずです。\nカメラ設定 シーンをキューブオブジェクトと一緒に動かしてみてください。何か見えましたか？ 3Dの場合、ゲームビューポートではCamera3Dを追加しない限り何も見えません。ルートノードに1つ追加し、カメラのギズモを使ってキューブを指すように位置を調整してください。\nカメラ上部にある淡い紫色のピラミッド状構造は「フラスタム」と呼ばれ、 これはカメラが捉えた視野を視覚的に表現したものです。画面左下に表示される小さな三角形の矢印に注目してください。これはカメラの「上方向」を表しています。カメラを動かしながら左上のプレビューボタンを押してみると、実際にカメラが捉えている映像を確認できます。シーンを動かしてみて、すべてが正常に動作していることを確認してください。\nまとめ 本チュートリアルでは、Godotの3Dエディターの使い方、以下のノードを追加する方法を学びました：Node3D、MeshInstance3D、および Camera3D、さらにギズモを使ってオブジェクトを配置する方法についてです。また、多くの新しい専門用語も学びました。圧倒されてしまわないよう、ご安心ください。\n次のセクションでは、3Dアセットをインポートして3Dシーンを構築する方法と、Godotのより高度な3Dノードの使い方を解説します。\n","description":"","tags":null,"title":"3Dエディター","uri":"/godot_recipes/4.x/ja/g101/3d/101_3d_01/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 3Dドライビング/レーシングゲームを作りたいが、どこから手をつければいいかわからない。\n解決策 メモ 3D環境においても、車両は基本的に地面に留まる性質があります。このため、移動処理の多くは実質的に2Dと同様の扱いができます。車の動作コードの大部分は、2D用カーステアリングレシピと非常に似た構造になります。このチュートリアルに進む前に、必ずそのレシピを確認しておくことをオススメします。\nGodotにはVehicleBodyノードが用意されており、これはRigidBodyをベースに、エンジン・ブレーキング・サスペンションなど複雑な物理挙動をシミュレートする機能を備えています。ただし、このアプローチは過剰な複雑さを伴うため、一般的なカジュアルレース／ドライビングゲームには不向きです。そこでここでは、CharacterBody3Dベースの解決策を採用します。\n情報 VehicleBody3D車両ボディの操作方法について詳しく知りたい方には、Bastiaan Olij氏によるこのシリーズ動画を強くオススメします。\n車両設定手順 コーディングを開始する前に、まず自動車の3Dモデルを見つけてGodotにインポートする必要があります。\nモデルのインポート方法 以下はこのデモンストレーションで使用する車両モデルです。\nメモ Kenny氏の『カーキット』でこの車種やその他のモデルを見つけることができます。以下のリンクから入手できます。 https://kenney.nl/assets/car-kit。キット全体をダウンロードすれば、後で他の車両も使用できます。\n車を読み込むには、\"Models/GLTF format\"フォルダ内で該当モデルを探してください。今回はsedanSports.glbを使用します。このファイルを新規Godotプロジェクトにインポートし、できればres://assets/cars/のような専用フォルダにまとめておくと良いでしょう。\nGodotでファイルを選択し、「インポート」タブに移動します。_ルートタイプ_を「CharacterBody3D」に変更し、「再インポート」をクリックします。これでこの車を使用する準備が整いました。\nキャラクター用 Body3D の設定 sedanSports.glbファイルをダブルクリックし、「新規継承」を選択してください。以下のように新しいシーンが作成されます。\n※各パーツごとの個別メッシュに注意。なお、余分な「tmpParent」Node3Dノードがありますが、こちらは無視して構いません。\nCharacterBody3D には衝突形状の欠落に関する警告メッセージが表示されています。まずはこの問題を修正する必要があります。\nCollisionShape を追加してください。 車両本体用に BoxShape を設定します。 前輪用と後輪用にそれぞれ1つずつ CylinderShape をおきます。 形状設定が完了したら、以下のような表示になるはずです。\nヒント 前面と背面の形状を一致させるには、どちらか一方を作成してサイズ設定した後、複製するだけで構いません。また、各ノードに適切な名前を付けることをオススメします。CollisionShapeノードには CollisionBody、CollisionWheelsFront、CollisionWheelsRearといった名称が適切でしょう。\n基本スクリプト 人間が操作する場合とAIが制御する場合の両方に対応できる自動車システムを構築したいです。どちらのケースでも、動作コードの大部分は共通化可能です - 実際には入力方法が異なるだけです。このため、両者で共有可能な基本カースクリプトを活用できます。\n新規スクリプトcar_base.gdを作成します。まずは変数の定義から始めてください。車の挙動を調整するための export 変数と、状態を追跡するためのその他の変数を用意します。\nextends CharacterBody3D # Car behavior parameters, adjust as needed @export var gravity = -20.0 @export var wheel_base = 0.6 # distance between front/rear axles @export var steering_limit = 10.0 # front wheel max turning angle (deg) @export var engine_power = 6.0 @export var braking = -9.0 @export var friction = -2.0 @export var drag = -2.0 @export var max_speed_reverse = 3.0 # Car state properties var acceleration = Vector3.ZERO # current acceleration var velocity = Vector3.ZERO # current velocity var steer_angle = 0.0 # current wheel angle ※ gravity 変数を使用する代わりに、「プロジェクト設定」でグローバル値を設定することもできます。別々に定義すれば、ゲームオブジェクトごとに異なる挙動を実現できます。最適な方法はプロジェクトの要件次第ですので、状況に応じて選択してください。\nengine_power と braking は車の加速・減速時に適用されます。\ndrag と friction についてはこちらで詳しく説明しています。\nスクリプトの残り部分は、2D版と非常に似た構造になりますが、Node3DとTransformを適切に動作させるために、変更が必要です。\nまず_physics_process()から見ていきます。\nここでは、コントロールを適用する前に車が地面に接地しているかを確認します。空中では操舵は不可能ですからね！その後、標準的な移動方程式を適用します。\n車が斜面から滑り落ちるのを防ぐ move_and_slide_with_snap() を使用している点に注意してください（トラックに坂道がある場合）。スナップ基準には車のローカル下方向ベクトルを使用しています。これも正しく坂道を処理するためです。\nfunc _physics_process(delta): if is_on_floor(): get_input() apply_friction(delta) calculate_steering(delta) acceleration.y = gravity velocity += acceleration * delta velocity = move_and_slide_with_snap(velocity, -transform.basis.y, Vector3.UP, true) この機能は摩擦力（車の速度に比例）と空気抵抗（速度の二乗に比例）を適用します。これによりパワーを加えていない時の減速効果が得られるだけでなく、車両の最高速度も決定されます。\nfunc apply_friction(delta): if velocity.length() \u003c 0.2 and acceleration.length() == 0: velocity.x = 0 velocity.z = 0 var friction_force = velocity * friction * delta var drag_force = velocity * velocity.length() * drag * delta acceleration += drag_force + friction_force 最後に、2次元自動車の場合と同じ簡略化された「自転車」モデルを用いて旋回角度を計算します。新たな速度が算出されたら、look_at()関数で車体を正しい方向に向けます。ここではドリフトやトラクションは考慮していません（これらについては後ほど扱います）。\nまた、車両の進行方向ベクトルと速度ベクトルの内積を計算することで、逆向き判定も可能になります。\nfunc calculate_steering(delta): var rear_wheel = transform.origin + transform.basis.z * wheel_base / 2.0 var front_wheel = transform.origin - transform.basis.z * wheel_base / 2.0 rear_wheel += velocity * delta front_wheel += velocity.rotated(transform.basis.y, steer_angle) * delta var new_heading = rear_wheel.direction_to(front_wheel) var d = new_heading.dot(velocity.normalized()) if d \u003e 0: velocity = new_heading * velocity.length() if d \u003c 0: velocity = -new_heading * min(velocity.length(), max_speed_reverse) look_at(transform.origin + new_heading, transform.basis.y) 最後に、車両の制御方法を決定する関数を作成します。これは個別の車両ごとにオーバーライドします。プレイヤーが操作する車両ではキーボード／ゲームパッド入力を、コンピュータが制御する車両ではAIによる判断を実装します。\nfunc get_input(): # Override this in inherited scripts for controls pass プレイヤー操作 これでプレイヤー操作を追加できるようになりました。以下が入力マッピングの設定です。\nアナログスティック付きゲームパッドをお持ちの場合は、ぜひそれをご使用になることを強くオススメします。キーボード操作ではオン/オフしか制御できないため、「ハンドル」を最大限に回転させるしかありません。アナログスティックを使えば、はるかに快適な操作体験が得られます。いずれの操作方法でもコードが正しく動作するよう、しっかり対応いたします。\n以下が車両にアタッチするスクリプトです： CharacterBody3D：\nextends \"res://cars/car_base.gd\" func get_input(): var turn = Input.get_action_strength(\"steer_left\") turn -= Input.get_action_strength(\"steer_right\") steer_angle = turn * deg2rad(steering_limit) $tmpParent/sedanSports/wheel_frontRight.rotation.y = steer_angle*2 $tmpParent/sedanSports/wheel_frontLeft.rotation.y = steer_angle*2 acceleration = Vector3.ZERO if Input.is_action_pressed(\"accelerate\"): acceleration = -transform.basis.z * engine_power if Input.is_action_pressed(\"brake\"): acceleration = -transform.basis.z * braking 最初に、ステアリング入力を取得します。これにより、値が -1 から 1 の範囲に収まります。この値を、最大許容角度に基づいてラジアン単位の角度に変換します。\n次のステップでは、ステアリング操作をより分かりやすく視覚的にフィードバックするため、ホイールメッシュを回転させます。通常、ドライバーは車からある程度離れた位置から見るため、効果を強調するために2倍に拡大しています。\nステアリング操作後、加速／ブレーキ入力を確認して車両のacceleration(加速度)を設定します。\nまとめ これは基本的な車コントローラーの実装です。ゲーム開発の出発点として自由にお使いください。さらに機能を追加したい場合は、以下に挙げるフォローアップレシピで扱うトピックを参考にしてください。\n牽引およびドリフト走行 チェイスカメラ制御とカメラ操作 AI／NPCコントロール（ステアリング、障害物回避、コース追従） 傾斜地やスロープの走行処理 メモ ダウンロードはこちら：https://github.com/kidscancode/3d_car_tutorial/releases\n関連レシピ 2D: Car Steering recipe Input Actions 3D: CharacterBody3D Movement この動画が気に入ったら？ ","description":"","tags":null,"title":"3Dで自動車を作ろう：ベースモデル","uri":"/godot_recipes/4.x/ja/3d/kinematic_car/car_base/index.html"},{"content":"ゲームエンジン ゲーム開発は非常に複雑で、多岐にわたる知識やスキルが求められます。 現代的なゲームを制作するには、実際のゲーム自体を構築する前に、 数多くの基盤技術を構築しておく必要があります。もし自分でコンピュータを一から組み立て、 独自のオペレーティングシステムを書いてからでないとプログラミングを始められないと想像してみてください。 その場合のゲーム開発は、まさに最初から すべて を自分で構築しなければならないようなものになるでしょう。\nさらに、あらゆるゲームに共通して必要となる機能が数多くあります。例えば： どのような種類のゲームであっても、画面上への描画処理は必ず必要になります。 もしその実装コードがすでに存在する場合は、一から書き直すよりも再利用した方が合理的です。これはまさにゲームエンジンが活躍する場面です。\nゲームエンジン とは、 ゲーム開発を支援するために設計された一連のツールと技術の集合体です。これにより、開発者は車輪を再発明するのではなく、 より効率的にゲーム構築に集中できるようになります。優れたゲームエンジンが提供する主な機能は以下の通りです。\nレンダリング（2D/3D） 「レンダリング」とは、プレイヤーの画面にゲームを表示するプロセスです。優れたレンダリングパイプラインでは、現代的なGPU機能、高解像度ディスプレイ、ライティングや透視投影といった効果を適切に処理しつつ、高いフレームレートを維持することが求められます。\n物理 正確で実用的な物理エンジンを開発するのは途方もなく困難な作業です。ほとんどのゲームでは何らかの衝突検出と応答処理が必要であり、さらに多くのタイトルが摩擦や慣性などのシミュレーションされた物理機能を要求します。しかし、実際にそのようなシステムを自ら実装しようとする開発者はごくわずかです。\nプラットフォーム対応 現在のゲーム市場では、モバイル、ウェブ、PC、コンソールなど、複数プラットフォーム向けにゲームをリリースできる能力が求められます。ゲームエンジンを使えば、一度開発したゲームをさまざまなプラットフォームにシームレスに移植・配信できます。\n開発環境 これらすべてのツールが単一アプリケーションに統合されており、あらゆる機能を一つの環境にまとめたことで、新しいプロジェクトごとに作業方法を学び直す必要がありません。\n現在、Unity、Unreal Engine、GameMaker Studioなど、数多くの人気ゲームエンジンが利用できます。これらの多くは商用製品であり、無料ダウンロード可能かどうか、またリリース時にライセンス契約やロイヤリティ支払いが必要になるかどうかを事前に確認することが重要です（特にゲームで収益を上げる予定の場合は注意が必要です）。利用するエンジンの利用規約を慎重に読み、その使用範囲と制約事項をしっかりと理解することが不可欠です。\nなぜGodotを選ぶべきか？ Godotをダウンロードするにはこちらをクリック\n上記のオープンソースゲームエンジンと比較して、Godot は完全に自由かつ無償で、非常に寛容なMITライセンスの下で公開されています。つまり、使用料や追加費用、ロイヤリティは一切発生しません。加えて、Godot は完全な機能を備えた最新型のゲームエンジンです。\n開発者にとって、Godotの利点は計り知れません。商用ライセンスによる制約がないため、ゲームの配信方法や配布先を完全に自由にコントロールできます。さらに、オープンソースという性質により、商用エンジンでは得られない透明性の高さも魅力です。例えば、特定の機能が要件を満たしていない場合、許可を得る必要なくエンジン自体を自由に修正できます。\n","description":"","tags":null,"title":"Godotとは？","uri":"/godot_recipes/4.x/ja/g101/start/101_01/index.html"},{"content":"課題 以下の3Dアセットセットをダウンロード（または作成）しました。リギング済みでアニメーション化されたキャラクターを含むファイル群で、これをGodotにインポートしたいです。\n解決策 本例では、セクションの説明 に記載されているアートパックをダウンロードして解凍済みであることを前提とします。\nプロジェクトにファイルをコピーする前に、以下の点に注意してください。アセットにはOBJ、FBX、GLTFといった複数の異なるファイル形式があります。また、変更を希望する場合に備えてサンプルや個別テクスチャなどの追加ファイルも用意されています。これらすべては不要であり、GodotではGLTF形式が最も推奨されるインポートフォーマットです。したがって、必ずgltfフォルダまたは.gltfファイル（もしくは同等のバイナリ形式である.glb）のみをプロジェクトディレクトリにドラッグ＆ドロップしてください。\nここで、「ダンジョン」パックからgltfフォルダと、「冒険者」パックからcharactersフォルダを取り出して、プロジェクトにドラッグしました。\nメモ ダンジョンパックには多くのファイルが含まれています。Godotがすべてを読み込むまで少し時間がかかるかもしれません！\nキャラクターのインポート方法 ファイルシステムタブでknight.glbファイルを選択し、左上のインポートタブをクリックしてください。\nこちらには基本的なインポート設定が表示されますが、より詳細なオプションも利用できます。詳細設定ボタンをクリックすると、新しいウィンドウが開きます。\n左側に表示されるのは、GLTFシーンに含まれる全てのデータです - テクスチャやアニメーションも含まれます。キャラクターに付属する各種武器オプションや、豊富なアニメーションリストに注目してください。\n中央に選択した文字のプレビューが表示され、右側には選択した項目の設定を調整できるオプションメニューがあります。\nプレイヤーが CharacterBody3Dとしてコード化されるため、ここでノードタイプを指定できます。シーンルートをクリックし、右側のパネルでルートの型を CharacterBody3Dに設定してください。\nアニメーション機能 アニメーション一覧までスクロールしてください。様々な種類がありますが、攻撃モーションのように一度再生すれば足りるものと、「待機中」や「走行中」などループさせたいものがあります。このようなアニメーションについては、名称を選択し、ループモードを「リニア」に設定します。これはすべての「歩行」「走行」「待機」バリエーションについて行います。作業が完了したら、画面下部の再インポートボタンをクリックしてください。\nアニメーションループの自動設定について 自作キャラクターを作成する場合、このステップは省略できます。ただし、アニメーション名がすべて\"-loop\"で終わるように命名してください。その他のインポート関連のヒントについては、Godotドキュメントの「インポートヒント」セクションを参照してください。\nファイルシステム内のknight.glbを右クリックし、新規継承シーンを選択してください。\nこのシーンではすべてのモデルとAnimationPlayer が表示され、ここではアニメーションを実際にテストできます。\nワールドアイテムのインポート 環境用オブジェクトのインポートも同様の手順で行います。具体例として、ダンジョンの壁ファイルを使いましょう。ダンジョンパックには多数のファイルが含まれているため、ファイル名フィルターに「wall」と入力して目的のファイルを簡単に見つけられるようにします。\n幸いなことに、Godotではインポート時に自動的にこれらの処理を行ってくれます。\nインポートウィンドウで、メッシュオブジェクトを選択します。右側の設定で「物理演算」ボックスをチェックし、「形状タイプ」を「単純な凸形」に設定してください（他のオプションも自由に確認してみてください）。\nをクリックします。ゲームでこのアセットを使用する際、Godotは自動的に対応する衝突形状を持ったStaticBody3Dを作成します。\n衝突形状の自動化設定 上記と同様に、衝突形状についてもインポート時の補助機能が用意されています。Blenderプロジェクトファイルに-col（またはその他のバリエーション）を追加することで、自動的にこの処理を実行するようインポートツールに指示できます。詳細はインポート補助機能のリンクを参照してください。\nインポート処理の自動化 自作アセットを作成する際にはインポートヒントを追加する方法が推奨されますが、今回のように既存のアセットパックをダウンロードする場合には適用できません。\n特定タイプのノードすべてに適用可能なインポートスクリプトを作成できます。例えば、前述した静的衝突判定オブジェクトの自動生成を自動化できます。\n以下のスクリプト例では、インポートされたオブジェクトのすべてのノードをループ処理し、各メッシュに対して静的衝突判定を作成します。\n@tool extends EditorScenePostImport func _post_import(scene): iterate(scene) return scene func iterate(node): if node != null: if node is MeshInstance3D: node.create_trimesh_collision() for child in node.get_children(): iterate(child) インポート タブでは、この設定をインポートスクリプトとして指定できます。その後、再インポートをクリックすると、衝突が作成されます。\nまとめ 以上で、Godotへの3Dアセットインポートに関する概要説明を終了します。\n以下のセクションを参照してください。セクション説明、インポートした3Dアセットを扱う際の具体例が記載されています。\n関連動画 ","description":"","tags":null,"title":"アセットのインポート","uri":"/godot_recipes/4.x/ja/3d/assets/importing_assets/index.html"},{"content":"課題 オブジェクトが破壊されたり収集された際に再生するために AudioStreamPlayer をモブ/コインなどに追加しました。ただ、対象のオブジェクトを削除すると、オーディオプレイヤーも同時に削除され、音が途切れる問題が発生します。より簡単に音声を管理できる方法を教えてください。\n解決策 この問題を解決するため、SceneTreeのどこからでもアクセス可能なノードを使用します。このノードは一連のAudioStreamPlayerノードと、再生するサウンドストリームのキューを管理します。\nスクリプトエディターで新しいスクリプトを作成します。\nextends Node var num_players = 8 var bus = \"master\" var available = [] # The available players. var queue = [] # The queue of sounds to play. func _ready(): # Create the pool of AudioStreamPlayer nodes. for i in num_players: var player = AudioStreamPlayer.new() add_child(player) available.append(player) player.finished.connect(_on_stream_finished.bind(player)) player.bus = bus func _on_stream_finished(stream): # When finished playing a stream, make the player available again. available.append(stream) func play(sound_path): queue.append(sound_path) func _process(delta): # Play a queued sound if any players are available. if not queue.empty() and not available.empty(): available[0].stream = load(queue.pop_front()) available[0].play() available.pop_front() このスクリプトをプロジェクト設定で 自動読み込み に設定してください。「AudioManager」のように、わかりやすく認識しやすい名前を付けてください。\nプロジェクト内で音を再生したい任意の場所で、以下を使いましょう。\nAudioManager.play(\"res://path/to/sound\") 覚えておくと便利ですが、サウンドファイルをテキストエディターに直接ドラッグ＆ドロップすることで、ファイルパスを簡単に貼り付けることができます。\nメモ この音声マネージャーは、SFXPlayer by TheDuriel の協力を得て適応されています\nサンプルプロジェクト例 以下に、オーディオマネージャーノードの使用例を示すサンプルプロジェクトをダウンロードできます。このプロジェクトでは、音声ファイルを格納したフォルダを読み込み、ボタングリッドを生成します。ボタンをクリックすると、対応するサウンドが再生されます。\n上部に、音声マネージャーのリアルタイム統計が表示されます。\n–\u003e\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/audio_manager\n","description":"","tags":null,"title":"オーディオマネージャー","uri":"/godot_recipes/4.x/ja/audio/audio_manager/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 シェーダーのコーディングをやってみたい。\n解決策 シェーダーとは、コンピュータのGPU（グラフィックスカード）上で動作する専用プログラムのことです。GPUは特定種類の演算処理を極めて効率的に実行できるよう最適化されています。このシェーダーコードをオブジェクトに適用することで、画面上でのレンダリング方法に影響を与えることができます。\nシェーダープログラムの出力結果は、オブジェクトを構成するピクセル群の色情報です。シェーダーは2D環境（canvas_item 用シェーダー）および3D環境（spatial シェーダー）で利用できます。\nシェーダーに関して初心者が特に理解しにくいのは、これが並列処理で行われるという点です。シェーダーは画面全体のすべてのピクセルに対して同時に実行されます。この仕組みにより処理速度が大幅に向上しますが、一方でシェーダー内でアクセス可能な情報にも制限が生じます。\nオブジェクトにシェーダーを追加するには、まず対象のオブジェクトの［マテリアル］プロパティを見つけ、「新規シェーダーマテリアル」を選択します。新しく作成したマテリアルをクリックして開くと、「新規シェーダー」を選択する画面が表示されます。さらにクリックすると、画面下部にシェーダー編集画面が開きます。\nシェーダーの最初の行にはそのタイプを指定が必要です。接続されているノードが2Dノードの場合は。\nshader_type canvas_item; もしくは3Dノードの場合：\nshader_type spatial; これらの初期例では2Dに限定して進めてください。まずSpriteノードを追加し、上記の手順に従ってシェーダーを適用してください。テクスチャにはGodotのデフォルトアイコンを使用することもできます。\nここで解説するシェーダーには主に2種類あります。 頂点シェーダー と フラグメントシェーダー です。\nフラグメントシェーダー フラグメントシェーダーはピクセルの色を計算します。具体例を見てみてください。\nvoid fragment() { COLOR = vec4(1.0, 0.0, 0.0, 1.0); } 全てのピクセルが赤色になります。COLORはフラグメントシェーダーの出力値であり、これをすべてのピクセルに同時に適用します。しかし、何らかのバリエーションを持たせたい場合はどうしますか？\nUV座標 シェーダーでは、ピクセル座標は UV 表記で指定されます。これらは正規化された値で、範囲は (0, 0)（左上）から (1, 1)（右下）までです。\nメモ シェーダーではベクトル型 (vec4) を使用してRGBAカラーを表現します。個々の成分には color.r のようにアクセスできます。色をベクトルとして扱うことで、ベクトル演算に基づく様々な興味深いエフェクトを実現できます。\nvoid fragment() { COLOR = vec4(UV.x, 0.0, 0.0, 1.0); } 現在の赤色チャンネルは、左側から右側にかけて0から1.0まで変化し、これはUV座標とともに変動します。\n別の例:\nvoid fragment() { COLOR = vec4(UV.x, 1.0 - UV.y, 0.5, 1.0); テクスチャ ピクセルカラーを直接設定するため、Godotアイコンのデータは破棄されています。テクスチャデータにはTEXTURE入力とtexture()関数を使用してアクセスできます。\nvoid fragment() { COLOR = texture(TEXTURE, UV); } これで元の画像に戻りました。各ピクセルの色は、それぞれのUV座標に対応するテクスチャの色値に設定されています。\nまた、COLOR出力の特定のチャンネルのみを変更することもできます。\nvoid fragment() { COLOR = texture(TEXTURE, UV); COLOR.a = 1.0 - UV.x; } この操作によりアルファチャンネルの値が低下し、フェードアウト効果が得られます。\n時間で変動させる もう一つの便利な組み込みシェーダープロパティはTIMEで、現在の経過時間を表す増加し続ける値を提供します。さらに、範囲が-1から1であるsin()関数も使用すると、以下のような効果が得られます。\nvoid fragment() { COLOR = texture(TEXTURE, UV); COLOR.a = abs(sin(TIME * 0.5)); } またはこちら：\nvoid fragment() { COLOR = texture(TEXTURE, UV); COLOR.a = max(0.0, UV.x - abs(sin(TIME))); } 頂点シェーダー 頂点シェーダーはオブジェクトの頂点を操作し、変形や拡大縮小できます。フラグメントシェーダーが各ピクセルごとに処理されるのと同様に、頂点シェーダーもオブジェクトの各__頂点__に対して実行されます。canvas_item シェーダーの場合、通常はテクスチャの4隅の頂点を指します。spatial シェーダーの場合は、メッシュの各頂点に対する処理となります。\n例えば、以下の例でどうなるか観察してみます。\nvoid vertex() { VERTEX.x += UV.x * 10.0; } このシェーダーでは、左側の2つの頂点 (0, 0) と (0, 1) は変更されず、右側の頂点がそれぞれ (10, 0) と / 10, 1) に変わります。\n頂点位置を時間経過に応じて変化させることで、さまざまな興味深い効果を生み出せます。\nvoid vertex() { VERTEX.y += sin(UV.x * TIME) * 10.0; } ユニフォーム シェーダーに値を渡すには、uniform キーワードで宣言された変数が必要です。この設定すると、インスペクターにはexport変数と似た形でその変数が表示されます。ただし注意点として、ユニフォーム変数の値はシェーダー内で変更することはできません！\n均一値（uniforms）はシェーダー全体でグローバルに使用可能であり、任意の関数からアクセスできます。\nヒント インスペクターで値を設定する際に補助として使用できるオプションの ヒント も利用できます。\nuniform float radius : hint_range(0, 1); 各種データ型に対応するヒントが用意されています。完全なリストについては シェーダー言語リファレンス を参照してください。\nまとめ これはシェーダーで実現できる機能のほんの一例に過ぎません。このセクションの他のレシピも参照して、プロジェクトで使えるテクニックを増やしていきます。\n関連するレシピ ","description":"","tags":null,"title":"シェーダー入門編","uri":"/godot_recipes/4.x/ja/shaders/intro/index.html"},{"content":"課題 2Dアニメーションを含むスプライトシートを使いたい。\n解決策 スプライトシートは2Dアニメーションの一般的な配布形式です。単一の画像ファイルに全てのアニメーションフレームを集約した形式で、効率的なデータ管理ができます。\nこのデモでは、Elthen氏制作の優れた「冒険者」スプライトを使用します。このアセットをはじめ、多くの高品質なアート素材は以下から入手できます。https://elthen.itch.io/\n警告 Godotが自動的に画像を切り分けできるように、スプライトシート内の画像は均一なグリッド状に配置してください。不規則に配置されている場合、ここで説明する手法は使用できませんのでご注意ください。\nノード設定 このアニメーション手法では、Sprite2Dノードを使用してテクスチャを表示し、その後AnimationPlayerでフレームの切り替えをアニメーション化します。これはあらゆる2Dノードで使用できますが、ここではデモンストレーション用にCharacterBody2Dノードを使用しています。\n以下のノードをシーンに追加してください。\nCharacterBody2D: Player Sprite2D CollisionShape2D AnimationPlayer Sprite2Dコンポーネントの Texture プロパティにスプライトシートテクスチャをドラッグ＆ドロップしてください。ビューポート内にすべてのスプライトシートが表示されます。個別のフレームに分割するには、インスペクターの「Animation」セクションを展開し、 Hframes を 13、 Vframes を 8に設定します。Hframes と Vframes は、それぞれ水平方向および垂直方向のフレーム数を指定するパラメーターです。\nFrame プロパティを変更してみて、画像がどのように変化するか見てみてください。これは後ほどアニメーション化するプロパティです。\nアニメーションの追加方法 以下の手順に従ってアニメーションを設定します。\n「AnimationPlayerコンポーネントを選択します。 「アニメーション」ボタンをクリックし、続いて「新規作成」を選択します。 新しいアニメーションに「idle」という名前を付けます。 アニメーションの長さを 2 に設定します。 「ループ」ボタンをクリックして、アニメーションが繰り返し再生されるようにします（詳細は以下を参照）。 スクラバーが0時点で、Sprite2Dノードを選択します。アニメーションのフレーム番号を0に設定し、値の隣にあるキーアイコンをクリックします。\nアニメーションを再生してみると、何も変化がないように見えます。これは最後のフレーム（12）が最初のフレーム（0）と全く同じ表示になっているためで、その間のフレーム（1～11）が全く反映されていないからです。これを修正するには、トラックの「更新モード」をデフォルト値の「離散」から「連続」に変更してください。このボタンはトラック右側の端に配置されています。\nこの方法が有効なのは、フレームがすでに順序通りに並んでいることが前提です。そうでない場合は、タイムライン上で各 Frame を個別にキーフレーム化が必要です。\n自由に他のアニメーションを追加してください。例えば「ジャンプ」アニメーションはフレーム 65～70 に設定されています。\n関連レシピ プラットフォームキャラクター ","description":"","tags":null,"title":"スプライトシートアニメーション","uri":"/godot_recipes/4.x/ja/animation/spritesheet_animation/index.html"},{"content":"課題 CharacterBody2DキャラクターがTileMapと衝突しており、どのタイルに衝突したのかを確認したい場合。\n解決策 オブジェクト同士が衝突した場合、衝突データは KinematicCollision2D オブジェクトとして取得されます。 TileMap は単一のコリダーとして機能するため、collider プロパティを参照すると実際にはこの TileMap ノードが返される点にご注意ください。\nその後、衝突位置にあるTileMapのタイルを特定が必要です。\n以下の状況を想定してください。変数 collision に KinematicCollision2D オブジェクトが格納されている場合：\n# Confirm the colliding body is a TileMap if collision.collider is TileMap: # Find the character's position in tile coordinates var tile_pos = collision.collider.world_to_map(position) # Find the colliding tile position tile_pos -= collision.normal # Get the tile id var tile_id = collision.collider.get_cellv(tile_pos) tile_idを取得した後、TileSetリソースからタイルのプロパティを取得できます。これはTileMapオブジェクトのtile_setプロパティで参照できます。例えば、特定のタイル名を取得するには以下のようにします。\nvar tile_name = collision.collider.tile_set.tile_get_name(tile_id) また、新しいidを設定することでタイルを変更することもできます。\ncollision.collider.set_cellv(tile_pos, new_id) 関連レシピ タイルマップ: オートタイルを使う タイルマップ: アニメーションタイル この動画が気に入ったら？ ","description":"","tags":null,"title":"タイルマップ：タイルを検出する","uri":"/godot_recipes/4.x/ja/2d/tilemap_collision/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 Godotがシーンツリー内のノードをどのように処理するかを理解しておく必要があります。\n解決策 「ツリー順序」はGodotの公式ドキュメントやチュートリアルで頻出する概念ですが、初心者にはその意味が直感的に理解しにくいものです。基本的に、ノードがツリー内で処理される順番は 上から下へ で、ルートノードを起点として、各ブランチごとに順番に下層へと降りていくのが原則です。\nシーンツリーの順序管理は、Godot初心者にとって非常に混乱しやすい要素です。この例では、各処理がどのような順番で実行されるかを具体的に解説します。\n以下にサンプルノード設定例をご紹介します。\n各ノードには以下のスクリプトがアタッチされています。\nextends Node func _init(): # Note: a Node doesn't have a \"name\" yet here. print(\"TestRoot init\") func _enter_tree(): print(name + \" enter tree\") func _ready(): print(name + \" ready\") # This ensures we only print *once* in process(). var test = true func _process(delta): if test: print(name + \" process\") test = false 結果について議論する前に、まず各コールバック関数が何を表しているのかを整理します。\n_init() はオブジェクトが最初に作成された際に呼び出されます。この時点でオブジェクトのインスタンスがコンピュータのメモリ上に確保されます。\n_enter_tree() はノードが初めてツリーに追加される際に呼び出されます（インスタンス化時や add_child() を使用した場合など）。\n_ready() メソッドは、ノードとそのすべての子ノードがツリーに追加され、準備が完了した時点で呼び出されます。\n_process() は毎フレーム（通常は 1 秒間に約 60 回）ツリー内の各ノードで呼び出されます。\nこれを単一ノードで単独に実行した場合、予想通りの順序になります。\nTestRoot init TestRoot enter tree TestRoot ready TestRoot process 子ノードも含めるとより複雑になります。以下が実行時の出力です。\nTestRoot init TestChild1 init TestChild3 init TestChild2 init TestRoot enter tree TestChild1 enter tree TestChild3 enter tree TestChild2 enter tree TestChild3 ready TestChild1 ready TestChild2 ready TestRoot ready TestRoot process TestChild1 process TestChild3 process TestChild2 process ご覧の通り、これらのノードはすべてツリー順に従って上から下へ、ブランチを先に表示しています。ただし_ready()コードは例外です。\n以下はノードリファレンスからの引用です。\nノードが「準備完了」状態になった際に呼び出されます。すべての子ノードがシーンツリーに組み込まれた時点で自ノードがトリガーされます。子要素がある場合、まず子ノードの _ready コールバックが実行され、その後に親ノードへ通知されます。\nよって、ノードを組み立てる時の重要な指針が導き出せます。\nヒント 親ノードは子ノードを管理するべきで、子ノードが親ノードを管理すべきではありません。\nこの要件を満たすためには、親ノード内のコードが子ノードのすべてのデータに完全にアクセス可能である必要があります。そのため、_ready() メソッドはツリー構造を逆順で処理する必要があります。\nこの原則は _ready() メソッド内で他のノードにアクセスしようとする際にも適用されます。ツリーを親ノード（あるいはさらに上層の祖先ノード）に移動する必要がある場合は、子ノードではなくその親ノード内で該当コードを実行する方が適切です。\n関連レシピ ノードパスの理解 ","description":"","tags":null,"title":"ツリー順序を理解しよう","uri":"/godot_recipes/4.x/ja/basics/tree_ready_order/index.html"},{"content":"基本的な使い方 Godotはもうダウンロードしましたか？以下のサイトから入手できます。\nhttps://godotengine.org\nGodot 4.0 へのアップデート準備中 【注 : この文章はオリジナル版の説明です】現在は、Godot 4.0対応版のGodot 101を開発中です。それまでの間、新規学習者の方にはGodot 3.xシリーズの使用をオススメします。このバージョンにはより多くの公式リソースや学習教材が揃っています。\n目次 ： Godotとは？ Godot エディター：操作方法の基本ガイド Godotノード's building blocks ","description":"","tags":null,"title":"はじめに","uri":"/godot_recipes/4.x/ja/g101/start/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 2Dプラットフォーマースタイルのキャラクターを作成したいです。\n解決策 経験が少ない開発者は、プラットフォームキャラクターをプログラミングするのがいかに複雑かを知って驚くことがよくあります。Godotには組み込みツールが用意されているものの、ゲームごとに解決策が異なるため、万能な答えはありません。このチュートリアルでは、ダブルジャンプやしゃがみ動作、ウォールジャンプ、アニメーションといった機能について詳しく掘り下げることはしません。ここではプラットフォーマー移動の基本原理に焦点を当てます。他の課題については、残りのレシピを参照してください。\nヒント RigidBody2D を使用してプラットフォーマーキャラクターを作成することはできますが、ここでは CharacterBody2D に焦点を当てます。運動制御ボディはプラットフォームゲームに最適です。このようなゲームでは、リアルな物理挙動よりも反応の良いアーケードライクな操作感が重視されるためです。\nまず CharacterBody2D ノードを作成し、その上に Sprite2D と CollisionShape2D を追加してください。\n以下のスクリプトをキャラクターノードのルートに追加してください。なお、ここでは[インプットマップ]で定義した入力アクション（\"walk_right\"、\"walk_left\"、および \"jump\"）を使用しています。詳細は InputActions をご覧ください。\nextends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 func _physics_process(delta): # Add gravity every frame velocity.y += gravity * delta # Input affects x axis only velocity.x = Input.get_axis(\"walk_left\", \"walk_right\") * speed move_and_slide() # Only allow jumping when on the ground if Input.is_action_just_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed speed、gravity、jump_speedに用いる値は、プレイヤースプライトのサイズに大きく依存します。このサンプルではプレイヤーテクスチャが 108x208 ピクセルです。もしスプライトが小さい場合は、より小さい値を使用が必要です。また、ゲーム操作が速く反応するように、適切な高数値を設定することが重要です。重力を低く設定すると浮遊感の強いゲームプレイになり、逆に高く設定するとすぐに地面に戻って再びジャンプできる状態になります。\n※ここで注意：move_and_slide() を使用した後に is_on_floor() をチェックしています。move_and_slide() 関数はこのメソッドの値を設定するため、その前にチェックすると前フレーム時点の値が取得されてしまう点にご注意ください。\n摩擦力と加速度について 上記のコードは優れた出発点であり、これを基盤として多種多様なプラットフォームコントローラーを実装できます。ただし一つ欠点として、移動が瞬間的に行われる点が挙げられます。より自然な操作感を得るには、キャラクターが最大速度まで徐々に加速し、入力がなくなると自然に減速して停止する仕組みの方が適切です。\nこの動作を実現する効果的な方法の一つが線形補間(lerp）を使用することです。移動中は現在速度と最大速度の間で補間し、停止時は現在速度から0まで補間します。補間量を調整することで、さまざまな移動スタイルを実現できます。\nヒント 線形補間の概要については、ゲーム数学：補間をご覧ください。\nextends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 @export_range(0.0, 1.0) var friction = 0.1 @export_range(0.0 , 1.0) var acceleration = 0.25 func _physics_process(delta): velocity.y += gravity * delta var dir = Input.get_axis(\"walk_left\", \"walk_right\") if dir != 0: velocity.x = lerp(velocity.x, dir * speed, acceleration) else: velocity.x = lerp(velocity.x, 0.0, friction) move_and_slide() if Input.is_action_just_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed friction と acceleration の値を変更して、ゲームの操作性にどのような影響があるか試してみます。例えば氷ステージの場合、これらの値を非常に低く設定することで、キャラクターの動きがより鈍くなるように調整できます。\n結論 このコードは、独自のプラットフォームコントローラーを構築するための基礎を提供します。壁ジャンプなどのより高度なプラットフォーマー機能については、本セクションの他のレシピを参照してください。\nプロジェクトのダウンロード プロジェクトコードはこちらからダウンロードしてください。https://github.com/godotrecipes/2d_platform_basic\n","description":"","tags":null,"title":"プラットフォームキャラクター","uri":"/godot_recipes/4.x/ja/2d/platform_character/index.html"},{"content":"課題 プレイヤーを追いかける敵が欲しい。\n解決策 敵をプレイヤー追跡モードに移行させる最初のステップは、敵が移動する必要のある方向を決定することです。ベクトル A から B への方向を求めるには、以下のように計算します。B - A。この結果を正規化すれば、方向ベクトルが得られます。\nこのアプローチは非常にシンプルです。毎フレーム、敵の速度ベクトルをプレイヤー方向へ向くように設定します。\nvelocity = (player.position - position).normalized() * speed GodotのVector2オブジェクトには、この処理を補助する組み込み機能があります。\nvelocity = position.direction_to(player.position) * speed しかし、これでは敵がプレイヤーから遠距離にいても追跡できてしまいます。これを修正するには、敵に Area2D を追加し、この「検出範囲」内にプレイヤーが入った場合にのみ追跡するようにします。\n以下にサンプルコードを示します。\nextends CharacterBody2D var run_speed = 25 var player = null func _physics_process(delta): velocity = Vector2.ZERO if player: velocity = position.direction_to(player.position) * run_speed move_and_slide() func _on_DetectRadius_body_entered(body): player = body func _on_DetectRadius_body_exited(body): player = null Area2Dからのbody_enteredシグナルとbody_exitedシグナルを接続しました。これで、敵が範囲内にいるか判別できるようになりました。\n上記の説明では、プレイヤーだけが出入りすることを想定しています。これは、適切な衝突レイヤーとマスクを設定することで実現できます。\nこの手法は他の種類のゲームにも応用できます。重要なのは、敵からプレイヤーへの方向ベクトルを求めることです。\nたとえば、ゲームがサイドスクロール形式であったり、移動に制限がある場合は、得られたベクトルの x 成分のみを使用して移動を判定できます。\n制約事項 この方法による移動は非常に単純化されています。壁などの障害物を回避したり、プレイヤーに近づきすぎて停止したりすることはありません。\n敵がプレイヤーに接近した際の対処はゲームデザインによって異なります。以下の選択肢が考えられます。\n2つ目の小さいエリアを追加し、そこで敵を足止めして攻撃させるか 接触時にプレイヤーを吹き飛ばすノックバック効果を実装するか さらに顕著な問題は、動きの速い敵キャラクターで発生します。プレイヤーが移動すると、このテクニックを使用する敵は瞬時に進行方向を変えます。より自然な動きを実現するためには、ステアリング挙動を使用することをオススメします。\nより高度な動作については、本書の他のレシピを参照してください。\n関連レシピ 見下ろし型のキャラクター移動 追跡ミサイル ","description":"","tags":null,"title":"プレイヤーを追いかける","uri":"/godot_recipes/4.x/ja/ai/chasing/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\nどこから始めればよいでしょうか？ゲームの種類やアイデアの具体性によって、答えは大きく異なります。この場合、事前にプロトタイプを作成し、さらにアイデアを固めておいたため、若干有利でした。とはいえ、当初の構想からは多少逸脱してしまいましたし、この連載も同様になるかもしれません。結果は時が経ってみないと分かりません。\n大きなプロジェクトの場合、まず設計書から始めるのが一般的です。これは、簡単なメモ1枚程度のものから、ゲーム世界・ストーリー・メカニクスの詳細を網羅した500ページに及ぶ大作まで様々です。ここではそこまで複雑なものは必要ありませんので、まずは大まかな企画内容をご説明します。\nゲームプラン このゲームでは、プレイヤーは「キャラクター」を操作し、円形のフィールドから別の円形へジャンプしていきます。ジャンプはクリックまたはタップで開始され、次のサークルに着地できなければゲームオーバーとなります。スコアは生存時間に応じて加算され、難易度は徐々に上昇していきます - 移動するサークルや、サイズが小さくなるサークル、さらには消滅するサークルが登場するためです。コンセプトは、スピード感あふれる短時間で遊べる、「もっと上を目指せ」という感覚を刺激するゲームプレイです。可能な限り、アートスタイルはシンプルでクリーンなデザインを維持し、視覚効果やオーディオエフェクトを駆使して魅力を高めていきます。\nメモ まずはGLES3を使用して開始します。これがどのような影響を及ぼすかはまだ明確ではありません。モバイルテストフェーズに移行した時点で、GLES2への移行が必要かどうかを検討します。\nまた、このプロジェクトを GitHubでフォローすることもできます。\nはじめに まず、プロジェクト設定から始めてください。画面サイズと動作仕様を定義が必要です。モバイルゲームとして開発するので、縦向き表示が必須となり、さまざまな端末の画面解像度に対応できるようが必要です。スマートフォンには数多くの画面解像度が存在するためです。\nプロジェクト設定を開き、［表示／ウィンドウ］セクションに移動します。画面サイズを (480, 854) に、［携帯端末／向き］を「縦」に、［伸縮／モード］を「2D」に、そして［伸縮／アスペクト比］を「維持」にそれぞれ設定してください。\n次に、［入力デバイス／ポインティング］セクションで「マウスからタッチ操作をエミュレート」を有効にしてください。これにより、画面のタッチイベントのみを使用してコードを入力できる上に、PCプラットフォームではマウスを使ったプレイも可能になります。\nプロジェクト管理体制 整理整頓のため、ゲームオブジェクト用のフォルダ（objects）とUI専用フォルダ（gui）を作成します。画像や音声などのゲームアセットはassetsフォルダに格納してください。開始時点で使用するアセットはこちらからダウンロードできます。\nメモ プロジェクトファイルをこちらからダウンロードしてください: circle_jump_assets.zip\nフォルダ構造とアセットの準備が整ったら、いよいよコーディング開始です！\nゲームオブジェクト 作成するゲームオブジェクトは2つあります。プレイヤー（「ジャンパー」）と円形オブジェクトです。\nジャンパー（Jumper） 移動と衝突判定には、Area2Dを使用することにします。公平を期すために言えば、ここではCharacterBody2Dを使用しても問題ありませんし、同様に機能します。ただし、このゲームでは厳密な衝突検出は必要なく、ジャンパーが円に接触したかどうかだけを把握できれば十分です。以下のノードを追加しましょう：\nArea2D (“Jumper”) Sprite CollisionPolygon2D VisibilityNotifier2D シーンを res://objects/ に保存し、円の画像ファイル（res://assets/images/jumper.png）をスプライトの Texture にドラッグします。すべてのゲーム画像は初期設定では白色になっていますので、後で動的に色付けする際に作業がしやすくなります。\nアートワークが上向きに描かれているため、Sprite2Dの［回転］プロパティを90に設定してください。\nCollisionPolygon2D を選択し、ジャンパーの三角形形状をカバーするように3点を追加してください。\nでは、ボディ部分にスクリプトを追加し、その動作をコーディングしていきます。\nまず、シグナルと変数について：\nextends Area2D var velocity = Vector2(100, 0) # start value for testing var jump_speed = 1000 var target = null # if we're on a circle 次に、画面タッチを検知し、円形オブジェクト上にいる場合はジャンプ処理を実行します。\nfunc _unhandled_input(event): if target and event is InputEventScreenTouch and event.pressed: jump() ジャンプすると、円軌道を離れて一定速度で前進します。\nfunc jump(): target = null velocity = transform.x * jump_speed 円領域への進入を検知するarea_enteredシグナルを利用するので、これを接続します。円に接触すると、前進動作を停止します。\nfunc _on_Jumper_area_entered(area): target = area velocity = Vector2() 円形フィールドに捕捉された場合、その周囲を回転したいと考えます。そこで円上にピボットポイントを追加し、その変形を適用することで、常に外側へ向き続けるようにします。それ以外の場合は、直線的に前進します。\nfunc _physics_process(delta): if target: transform = target.orbit_position.global_transform else: position += velocity * delta カラーシェーダー ヒント シェーダーを使い始める方法については、「[シェーダー]（/godot_recipes/4.x/ja/shaders）」セクションを参照してください。\n以下の手順で進めてください。\nSprite にカスタムカラーを適用するため、小さなシェーダーを使用します まずSpriteを選択してから、Material プロパティで新しいShaderMaterialを追加します 追加した Shader をクリックし、[シェーダー]メニューで「新規シェーダー」を選択、さらにそれをクリックしてください シェーダーエディタパネルが画面下部に表示されます 以下はカラーシェーダーのコードです。ここでは色を指定するために uniform 変数を使用しており、インスペクターから直接設定するか、ゲームスクリプトから値を割り当てることができます。その後、テクスチャ上のすべての表示ピクセルがその色に置き換えられ、アルファ（透明度）値は保持されます。\nshader_type canvas_item; uniform vec4 color : hint_color; void fragment() { COLOR.rgb = color.rgb; COLOR.a = texture(TEXTURE, UV).a; } インスペクターに新たに「シェーダーパラメーター」セクションが表示され、カラー値を設定できるようになります。\nこのシェーダーは他のシーンでも使用するため、［シェーダー］プロパティで「保存」を選択し、res://objects/color.shader として保存してください。\nサークル（Circle） 2 つ目のゲームオブジェクトは円です。ゲームが進むにつれて何度もインスタンス化されます。最終的には移動や縮小など様々な動作を追加しますが、今回は最初の実装として、単にプレイヤーを捕捉する機能だけを実装します。\n以下は開始ノードの設定です。\nArea2D (“Circle”) Sprite CollisionShape2D Node2D (“Pivot”) Marker2D (“OrbitPosition”) 「ピボット」ノードを使って、プレイヤーが円軌道を描くようにします。「軌道位置」は円のサイズに応じてオフセットされ、プレイヤーはこの位置に追従します。\nres://assets/images/circle1_n.png を Sprite のテクスチャとして使いましょう。そのついでに、先ほど作成した color.shader を使用するために、ShaderMaterial を追加し、「読み込み」を選択してください。\nCollisionShape2D に円形状を追加し、ルートノードにスクリプトをアタッチしてください。\nextends Area2D @onready var orbit_position = $Pivot/OrbitPosition var radius = 100 var rotation_speed = PI func _ready(): init() func init(_radius=radius): radius = _radius $CollisionShape2D.shape = $CollisionShape2D.shape.duplicate() $CollisionShape2D.shape.radius = radius var img_size = $Sprite.texture.get_size().x / 2 $Sprite.scale = Vector2(1, 1) * radius / img_size orbit_position.position.x = radius + 25 func _process(delta): $Pivot.rotation += rotation_speed * delta init()関数では、与えられた radius に基づいて円のサイズを設定します。衝突形状のサイズも、テクスチャのサイズに合わせて調整する必要があります。\nテスト用に radius の値を変えてシーンを実行してみてください。（後で _ready() 内で init() を呼び出す処理は停止します）\nメインシーン これで動作テストが可能になりました。\nNode2D を使用して「メイン」シーンを作成し、その中にジャンパーコンポーネントとサークルをインスタンス化してください。ジャンパーがサークルに衝突するように配置します（デフォルトのジャンプ速度は (100, 0) です）。\n試走してみてください。ジャンパーが円の内側に捕捉され、軌道を描き始めるはずです。マウスをクリックすると、ジャンプ台はその方向に向かって発射されます。\nGitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\nこの動画が気に入ったら？ ","description":"","tags":null,"title":"プロジェクトの設定","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_01/index.html"},{"content":"この最初のゲームプロジェクトでは、初めてGodot Engineでゲームを作成する手順をご案内します。事前の経験は必要ありませんが、少なくともはじめてのGodotセクションに目を通していることが望ましいです。ここでは、エディターのインターフェースやGodotのUIの操作方法について学べます。\nなぜ2Dから始めるべきか？ 一言で言えば、3Dゲームは2Dゲームと比べて格段に複雑です。しかし、理解すべき基本エンジン機能の多くは同じものです。Godotの開発ワークフローをしっかり習得するまでは、2Dプロジェクトに専念することをオススメします。その段階までくれば、3Dへの移行はずっとスムーズに感じられるはずです。\nGodotを起動して新規プロジェクトを開始します。名前は自由に決めて構いません - ここでは「クラシック・シューティング」と名付けます。これは伝統的なシューティングゲームスタイルの作品となるからです。\nアートワークのダウンロード方法 ゲームで使用するアートワークは、itch.ioからダウンロードできます。 Grafxkid氏によるミニピクセルパック\nアートワークパックを解凍し、プロジェクトにコピーするには、フォルダを[ファイルシステム]タブにドラッグアンドドロップしてください。\nプロジェクト設定 次に必要なのは、プロジェクト全体の設定をセットアップすることです。プロジェクト設定を開き、右上にある「詳細設定」トグルを有効にしてください。\n表示/ウィンドウ セクションにおいて：\nViewport Width と Viewport Height を 240, 320 に設定。 Window Width Override と Window Height Override を 480, 640 に設定。 Stretch/Mode を canvas_items に指定。 これらの設定により、ゲームが適切なサイズで表示されるようになります。ピクセルアートを使用しているため、画像自体は極端に小さく、古い機種向けの解像度である240x320でも問題なく表示されます。ただし現代のモニターではこのサイズは画面に対してかなり小さいため、他の設定で比例的に拡大表示できます。1080pモニターをお使いの場合は、オーバーライド値を720x960に変更することもできます。また、ゲーム実行中にウィンドウサイズを変更することもできるようになります。\nキャンバステクスチャセクションのレンダリング/テクスチャ設定で、デフォルトテクスチャフィルタを Nearest に設定してください。これにより、美しいピクセルアートが鮮明なまま保たれ、左側ではなく右側のようなきれいな表示になります。 プロジェクト設定 画面の上部にある インプットマップ タブをクリックしてください。ここではゲーム内で使用する入力設定します。「新規アクション追加」欄に以下を入力し、各入力項目を追加したら\u003cEnter\u003eキーを押してください。right(右)、left(左)、up(上)、down(下)、shoot(射撃)。それぞれの入力にキーボードショートカットを割り当てるには、対応する + ボタンをクリックした後、キーボードでそのキーを押してください。作業が完了したら、以下のような設定になっているはずです。 他のキー設定を使いたい場合は、自由に変更してください。\n次のステップ 設定は完了しました。いよいよ開始できます！次のセクションでは、プレイヤーが操作する宇宙船を作成します。\n戻る 次へ ","description":"","tags":null,"title":"プロジェクト設定","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_01/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 画面にテキストを表示したい。\n解決策 スクリーンにテキストを表示する機会が増えるでしょう。例えば、タイトル表示やカウントダウンタイマー、スコアカウンターなどがその一例です。これらのほとんどは、Godotの Label ノードを使えば簡単に実装できます。\nフォント操作について はじめに、フォントファイルが必要になります。Godotのフォントサポートについては別のレシピで詳しく解説しますが、ここではTTFまたはOTF形式のフォントファイルを前提とします。ビットマップフォントを使用する場合は、関連するレシピを参照してください。\nメモ この例では、人気のある無料フォント「Roboto」を使用します。このフォントはGoogle Fontsで入手できます。こちらからもダウンロードできます。 Roboto_font.zip\nラベルの追加方法 シーンに新しい Labelノードを追加してください。インスペクターでは、ほとんどのプロパティが自明な内容になっています（マウスカーソルを合わせると説明が表示されます）：\n「テキスト」フィールドに任意の文字を入力して、表示スタイルを試してみます。デフォルトフォントが設定されていますが、かなりシンプルな（しかも小さい）デザインになっています。\nDynamicFontの追加方法 フォントを追加するには。インスペクターで「カスタムフォント」セクションまでスクロールダウンし、展開してください。空の「フォント」プロパティで『新規ダイナミックフォント』を選択し、新しく表示されたDynamicFontをクリックしてさらに展開します。\nフォントファイル（この例ではRoboto-Medium.ttfを使用しています）をフォントデータプロパティにドラッグするか、「読み込み」を選択して直接ファイルを指定します。調整すべきプロパティは複数ありますが、まずはサイズを少し大きくしてみます。\nテキストの表示に与える影響を自由に調整してみてください。例えば、以下の画像では、2番目のラベルにフィルタープロパティが有効になっています。\n色の調整 ラベルのフォントカラーは「カスタムカラー」セクションで調整できます。ここでは「フォントカラー」を変更できるほか、影の色を追加することもできます。影のプロパティは「カスタム定数」セクションで設定します。\n動的に変化するテキスト表示 シーンに静的テキストのみが必要な場合、ここで完了です。ただし、ラベルを動的に更新する必要がある場合は、コード内でtextプロパティを使用して実装できます。\nTimerノードがシーンに含まれている場合、以下のように操作できます。\nextends Control var counter = 0 func _ready(): $Label.text = str(counter) func _on_Timer_timeout(): counter += 1 $Label.text = str(counter) ラベルの使用例やUIノードとの連携方法については、関連レシピセクションをご覧ください。\n関連レシピ この動画が気に入ったら？ ","description":"","tags":null,"title":"ラベル","uri":"/godot_recipes/4.x/ja/ui/labels/index.html"},{"content":"課題 クラシックな2Dゲーム（『パックマン』タイプの作品など）でよく使われる、プレイヤーキャラクターが画面端から反対側に移動する機能を実装したいです。\n解決策 スクリーンサイズ（ビューポート）を取得します。\n@onready var screen_size = get_viewport_rect().size get_viewport_rect() は CanvasItem の派生ノードであればどれでも利用できます。\nプレイヤーの座標を比較します。\nif position.x \u003e screen_size.x: position.x = 0 if position.x \u003c 0: position.x = screen_size.x if position.y \u003e screen_size.y: position.y = 0 if position.y \u003c 0: position.y = screen_size.y 注意：この処理ではノードのpositionを使用しています。これは通常、スプライトまたはボディの中心座標を指します。\nwrapf() を使用して、シンプルに実装します。\n上記のコードは、GDScriptのwrapf()関数を使用することで簡略化できます。この関数は指定された範囲内で値を「ループ」させます。\nposition.x = wrapf(position.x, 0, screen_size.x) position.y = wrapf(position.y, 0, screen_size.y) ","description":"","tags":null,"title":"画面ループ","uri":"/godot_recipes/4.x/ja/2d/screen_wrap/index.html"},{"content":"課題 オブジェクトが画面内に表示、また非表示になるタイミングを検知したい。\n解決策 この目的のためのノードが用意されています。VisibleOnScreenNotifier2D。このノードをオブジェクトにアタッチすれば、screen_enteredシグナルとscreen_exitedシグナルを利用可能になります。 *\n例 1 発射後に直線軌道で移動する投射物を考えてみます。継続的に射撃を続けると、画面上から外れた物体であってもエンジンが追跡すべき対象が大量に発生し、結果的にラグの原因となる可能性があります。\n以下に発射体の移動コードを示します。\nextends Area2D var velocity = Vector2(500, 0) func _process(delta): position += velocity * delta プロジェクトイルが画面外に移動した際に自動的に削除されるようにするには、VisibleOnScreenNotifier2Dを追加し、そのscreen_exitedシグナルを接続してください。\nfunc _on_VisibleOnScreenNotifier2D_screen_exited(): queue_free() 例2 敵キャラクターがいて、経路に沿って移動したりアニメーションを再生したりするなどの動作を行います。大規模なマップで多数の敵が存在する場合、同時に画面上に表示されるのはそのうちのわずか数人だけです。VisibleOnScreenNotifier2D を使用することで、オフスクリーン状態の間だけ敵の動作を無効化できます。\nコードの一部：\nvar active = false func _process(delta): if active: play_animation() move() func _on_VisibleOnScreenNotifier2D_screen_entered(): active = true func _on_VisibleOnScreenNotifier2D_screen_exited(): active = false ","description":"","tags":null,"title":"画面への入力／出口","uri":"/godot_recipes/4.x/ja/2d/enter_exit_screen/index.html"},{"content":"課題 2D見下ろし方式のゲームを開発しており、キャラクターの動きを制御が必要です。\n解決策 ここでは、以下の入力アクションが定義されているとします。\nアクション キー \"up\" W,↑ \"down\" S,↓ \"right\" D,→ \"left\" A,← \"click\" マウスボタン1 またここでは、CharacterBody2D ノードを使用していると想定します。\nこの問題は、求める行動の種類に応じてさまざまな方法で解決できます。\nその1：8方向移動方式 このシナリオでは、プレイヤーは4方向キー（斜め移動含む）を使って操作します。\nextends CharacterBody2D var speed = 400 # speed in pixels/sec func _physics_process(delta): var direction = Input.get_vector(\"left\", \"right\", \"up\", \"down\") velocity = direction * speed move_and_slide() その2: 回転と移動を組み合わせる場合 この操作方法では、左右でキャラクターを回転させ、上下で向いている方向に前進・後退します。これは「アステロイド風」と呼ばれる伝統的な操作方式です。\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec var rotation_speed = 1.5 # turning speed in radians/sec func _physics_process(delta): var move_input = Input.get_axis(\"down\", \"up\") var rotation_direction = Input.get_axis(\"left\", \"right\") velocity = transform.x * move_input * speed rotation += rotation_direction * rotation_speed * delta move_and_slide() メモ Godotでは角度 0 度をx軸に沿っていることを意味します。これは、ノードの前方方向（transform.x）が右向きであることを示しています。キャラクタースプライトも同様に、右側に向かって描画されるように設定してください。\nその3：マウスで照準を合わせる方法 オプション2と同様ですが、今回はキャラクターの向きをマウスで操作できます（常にマウス方向へ向いています）。前後移動はこれまで通りキーボードキーで行います。\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec func _physics_process(delta): look_at(get_global_mouse_position()) var move_input = Input.get_axis(\"down\", \"up\") velocity = transform.x * move_input * speed move_and_slide() その4：クリックして移動 このオプションでは、キャラクターがクリックした位置に移動します。\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec var target = null func _input(event): if event.is_action_pressed(\"click\"): target = get_global_mouse_position() func _physics_process(delta): if target: # look_at(target) velocity = position.direction_to(target) * speed if position.distance_to(target) \u003c 10: velocity = Vector2.ZERO move_and_slide() ターゲット位置に近づくと移動を停止します。これを行わないと、キャラクターは「ぐらぐら」動きながら、少しずつ目標を越えては戻り、再び越えて…という動作を繰り返します。オプションとして、look_at() を使って移動中の方向を向くようにすることもできます。\nプロジェクトのダウンロード プロジェクトコードをダウンロードするにはこちら：https://github.com/godotrecipes/topdown_movement\n","description":"","tags":null,"title":"見下ろし型での移動","uri":"/godot_recipes/4.x/ja/2d/topdown_movement/index.html"},{"content":" 初めての2Dゲーム 2Dシューティングゲーム開発でGodotの基本を学ぼう。 このシリーズでは、基礎からスタートして、クラシックな旧式の宇宙シューティングゲームを開発していきます。\n以下は完成したゲームのスクリーンショットです。\nこのシリーズでは、各エピソードごとにゲームの一部を構築しながら、機能を追加し、その過程を詳しく解説していきます。\n背景 プログラミングが難しいと思っている人は、以下のリソースを先に参照してください。\n初めてのGodot: GDScript入門 - 当サイトのチュートリアル Godot公式ドキュメント - 公式チュートリアル このプロジェクトを GitHubからダウンロード プロジェクトコードはこちらからダウンロードしてください。\nhttps://github.com/godotrecipes/classic_shmup\n","description":"","tags":null,"title":"初めての2Dゲームを作ろう","uri":"/godot_recipes/4.x/ja/games/first_2d/index.html"},{"content":"以下に最新追加されたレシピをご紹介します。\nキャラクターとリジッドボディの相互作用 CharacterBody3D：サーフェスに沿わせる動作 CharacterBody3D：移動操作例 アーケードスタイルの自動車 2Dグリッド上での経路探索 3.xからの移行ガイド レイキャストを使用した射撃システム FPSキャラクターのモデル RigidBody2D：ドラッグ＆ドロップ操作 2D自動車のステアリング制御 3D空間に浮かぶHPバー グリッドベースの移動 アーケードスタイル3D宇宙船モデル 補間機能付きカメラ プラットフォーム用キャラクターモデル ","description":"","tags":null,"title":"新着レシピ","uri":"/godot_recipes/4.x/ja/recent/index.html"},{"content":"概要 ゲームの動作やメカニクスを構築するには、スクリプトを記述してノードやその他のオブジェクトにアタッチします。例えば、Sprite2Dノードは自動的に画像を表示しますが、画面上で移動させるには、速度や移動方向などを制御するスクリプトを追加する必要があります。\nこれはインスペクターを使用する場合と同様と考えてください。GDScriptはGodotノードに関するあらゆる知識を持ち、それらへのアクセス方法を知っているだけでなく、動的に変更することもできます。\nGodot公式ウェブサイトのGDScriptドキュメントは、言語の基本を理解するための最適なリソースです。時間を取ってしっかり読み込むことを強くオススメします。\nGDScript は Python ですか？\n「GDScriptはPythonをベースにしている」と説明されているのをよく目にするでしょう。これは一見誤解を招きやすい表現ですが、実際にはGDScriptはPythonに似た 構文 を採用してはいるものの、Godotエンジン向けに最適化された完全に独立した言語です。とはいえ、すでにPythonをある程度知っている方なら、GDScriptは非常に親しみやすいものに感じられるはずです。\n警告 多くのチュートリアル（およびGodot自体）では、ある程度のプログラミング経験があることを前提としています。これまで一度もコードを書いたことがない場合、Godotを学ぶのは困難に感じるかもしれません。ゲームエンジンを習得するだけでも大きな挑戦ですが、同時にコーディングも学ぶとなると、負担はさらに大きくなります。もしこのセクションのコードで苦戦しているなら、初心者向けプログラミング講座（Pythonがオススメです）を履修することで、基本的な概念をしっかりと理解できるはずです。\nスクリプトの構成要素 GDScriptファイルの最初の行はextends \u003cクラス名\u003eで始めなければなりません。ここで\u003cクラス名\u003eは、既存の組み込みクラスまたはユーザーが定義したカスタムクラスのいずれかを指定します。たとえば、CharacterBody2Dノードにスクリプトをアタッチする場合、スクリプトの冒頭はextends CharacterBody2Dとなります。これは、スクリプトが組み込みのCharacterBody2Dオブジェクトの持つ機能をすべて引き継ぎ、さらにユーザー定義の追加機能で拡張していることを意味します。\nスクリプトの残り部分では、任意数の変数（通称「クラスプロパティ」）と関数（通称「クラスメソッド」）を定義できます。\nスクリプトの作成方法 最初のスクリプトを作成してください。覚えておくべきは、どのノードにもスクリプトをアタッチできるということです。\nエディターを開き、空のシーンに Sprite2Dノードを追加してください。新規追加したノードを右クリックし、「スクリプトをアタッチ」を選択します。または、検索ボックス横のボタンをクリックしてスクリプトをアタッチすることもできます。\n次に、スクリプトを保存する場所とファイル名を決定する必要があります。ノードに名前を付けている場合、スクリプトは自動的にそれに一致する名前が割り当てられます（変更を加えない限り、このスクリプトはおそらく「sprite2d.gd」という名前になります）。\nスクリプトエディターウィンドウが開きます。ここに、新しく作成した空のスプライト用スクリプトが表示されます。Godotは自動的に数行のコードと、各コードの機能を説明するコメントを自動生成しています。\nextends Sprite2D # Called when the node enters the scene tree for the first time. func _ready(): pass # Replace with function body. # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): pass スクリプトが Sprite2D に追加されたため、最初の行は extends Sprite2D に自動的に設定されます。このスクリプトは Sprite2D クラスを継承しているため、Sprite2Dノードが提供するすべてのプロパティとメソッドにアクセスして操作することが可能になります。\nプロパティとメソッド プロパティ と メソッド は、オブジェクト内で定義された 変数 および 関数 を指す専門用語です。プログラマー間では、これらの用語がしばしば混同されて使用されています。\nその後、スクリプトで使用するすべての変数（いわゆる「メンバ変数」）を定義します。変数は 「var」 キーワードで宣言します - コメント例からもわかるように。\nどうぞコメントを削除して、次の部分について話してください。\nここで _ready() という関数が定義されています。GDScriptでは関数を「func」キーワードで宣言します。この_ready()関数はGodotが特にチェックする特別な関数で、ノードがツリーに追加されるたびに実行されます - 例えば「再生」ボタンを押した時などに呼び出されます。\nゲーム開始時にスプライトを特定の位置に配置したいとします。インスペクターでは「位置」プロパティを設定する必要があります。「Node2D」とラベル付けされたセクションにある点に注目してください。これは、これは任意の Node2Dタイプのノードが持つプロパティであり、Sprite2Dに限定されないことを示しています。\nコード内でプロパティを設定するには？1つの方法として、インスペクターでプロパティの上にマウスカーソルを合わせると、名前を確認できます。\nGodotには非常に便利な組み込みヘルプ/リファレンスツールが用意されています。スクリプトウィンドウ上部の「クラス」をクリックし、Node2Dを検索すると、該当クラスで利用できるすべてのプロパティとメソッドが記載されたヘルプページが表示されます。少し下にスクロールすると、探していた「メンバ変数」セクションにpositionが表示されているはずです。また、このプロパティが「Vector2」型であることも明記されています。\nスクリプトに戻ってそのプロパティを使いましょう。\nfunc _ready(): position = Vector2(100, 150) エディターが入力に応じて即座に提案を表示しているのに注目してください。Godotは多くの場面でベクトルを使用しており、この点については後ほど詳しく説明します。まずは「Vector2」と入力してみます。ヒントが表示されるので、x と y には2つの浮動小数点数を指定する必要があることがわかります。\nこれで「このスプライトが起動したら、その位置を (100, 150) に設定する」というスクリプトが完成しました。このコードを試すには、『シーンを実行』ボタンをクリックしてみます。\n学習のコツ プログラミングを始めたばかりの人が「これらすべてのコマンドをどうやって覚えればいいですか？」とよく質問します。他のどんなスキルとも同様に、これは暗記ではなく練習次第なのです。使えば使うほど、頻繁に使う機能は自然と身につき、無意識に扱えるようになります。それまでは、リファレンスドキュメントをすぐに参照できるようにしておくことをオススメします。わからないものに出会ったら、いつでも検索機能を使います。複数モニターを使用している場合は、Webドキュメントを横に開いておき、素早く確認できる状態にしておくと便利です。\nまとめ GDScriptで最初のスクリプトを作成できました！次に進む前に、このステップで行った内容を完全に理解しておいてください。次回では、スプライトを画面上で移動させるためのコードを追加していきます。\n","description":"","tags":null,"title":"入門","uri":"/godot_recipes/4.x/ja/g101/gdscript/gdscript_01/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\nはじめに このデモでは、見下ろし型の迷路ゲームを例に、ローカルマルチプレイヤーゲームを考えます。このゲームでは2人のプレイヤーが参加し、一方は矢印キー、もう一方はWASDキーで操作します。これは問題ありませんが、もしゲーム世界全体が1画面に収められる程度の大きさであれば、特に問題はありません。しかし、マップが非常に広い場合、両プレイヤーを個別に表示する「分割画面」ビューが必要になるでしょう。\nまた、ミニマップ表示をすばやく設定する方法についても解説します。\nゲーム設定 ゲーム世界のセットアップに多くの時間をかけるつもりはありません。登場する2人のキャラクターは シンプルな8方向移動を実装したCharacterBody2D オブジェクトです。\nメモ このパーツのセットアップでお困りの場合は、公式Godotドキュメントの以下のセクションをご覧ください: 2D移動の概要。\n各操作は、プロジェクト設定の[インプットマップ]セクションで個別に設定されています。「right_1」は右矢印キー、「right_2」はDキーなどです。このように命名することで、コード内で以下の構文を使用でき、開発効率を大幅に向上させられます。\n@export var id = 0 func get_input(): velocity = Vector2() if Input.is_action_pressed('right_%s' % id): velocity.x += 1 # etc. この方法であれば、キャラクターが同じ移動スクリプトを使用できます。各プレイヤーに適切な値を id として割り当てるだけです。\n以下の手順に従って、2つのプレイヤーを TileMap を含む「World」シーンに追加してください。\nご希望であれば、ワールドが既に設定済みの開始プロジェクトをこちらからダウンロードできます。\nsplitscreen_start.zip\nこのマップはゲーム画面よりもはるかに大きいことに注意してください。ただ、それ以外の点ではすべて正常に動作します。このようにゲームの「World」を個別に設定することで、ビューポートの設定が格段に容易になり、より柔軟に扱えるようになります。\nビューポート、カメラ、およびワールドについて まず、2つのビューポートを含む新しいシーンを作成します。 ルートノードとして使用するノードを作成します。通常、Nodeを使用します。このノードには独自のプロパティが何もないため（単にシーンの他の要素を保持するためのものです）、使い勝手が良いからです。\n各ビューポートノード（Viewport）には位置情報が含まれません（Node3DやNode2Dを継承していません）。ここでは、各ビューポートを管理するためにViewportContainerを使用します。このコンテナはControlノードの一種です。それらを横並びに配置するためには、HBoxContainerを使います。\nHBoxContainerの配置を「中央」に設定し、2つのビューポート間に小さな隙間を設けるには、_カスタム定数/間隔_に 5 を設定してください。「Layout(レイアウト)」メニューでは「Full Rect」を選択します。\n次に、2つのViewportContainerを子要素として追加し、それぞれに2と1という名前を付けます（これらは表示するプレイヤーに対応するためです）。両方のコンテナについて Size Flags を「Fill, Expand」に設定してください。これにより、各コンテナが画面の半分を埋めるように拡大されます。さらに、 Stretch プロパティもチェックすることで、Viewportが自動的にコンテナのサイズに合わせて調整されるようになります。\n各コンテナ内に、Viewport 要素を追加してください。なお、ビューポートの Size プロパティを設定した場合、その値はコンテナによってリセットされますのでご注意ください。\nViewportにコンテンツを表示するには、Camera2Dが必要です。このカメラはViewport上にレンダリングを行います。各ビューポートに1つずつ追加してください。また、カメラを有効にするには 現在のプロパティを必ずチェックしてください 。さらに、各カメラのズーム値を(0.75, 0.75)に設定することで、プレイヤー周辺のエリアをより詳細に表示できるようになります。\nノードを以下のようにします。\n┖╴Main (Node) ┖╴Viewports (HBoxContainer) ┠╴ViewportContainer2 ┃ ┖╴Viewport2 ┃ ┖╴Camera2D ┖╴ViewportContainer1 ┖╴Viewport1 ┖╴Camera2D メモ 注意点：ViewportContainer1をHBoxContainer内で2番目に配置しました。この設定により、プレイヤー1が矢印キーを使用するため、コンテナは右側に表示されます。\n空間（World）の追加 シーンを実行すると、ビューポートには何も表示されません。これはビューポートがレンダリングする「世界」を持っていないためです。3Dの場合はworldプロパティ、2Dの場合はworld_2dプロパティが、ビューポートの環境設定を表し、カメラによって何が表示されるかを決定します。ワールドはコード内で設定可能ですが、2Dの場合、追加した子ノードも自動的に表示される点に注意が必要です。\n以下のように、「World」シーンをViewport1の子としてインスタンス化します。これでシーンを実行すると、左ビューポート内に世界が表示されます。\nまた、 Viewport2に世界を追加が必要ですが、同じ世界を使用させたい場合はどうすればよいでしょうか。 これはコードで処理できます。Main にスクリプトをアタッチし、以下を追加してください。\nextends Node @onready var viewport1 = $Viewports/ViewportContainer1/Viewport1 @onready var viewport2 = $Viewports/ViewportContainer2/Viewport2 @onready var camera1 = $Viewports/ViewportContainer1/Viewport1/Camera2D @onready var camera2 = $Viewports/ViewportContainer2/Viewport2/Camera2D @onready var world = $Viewports/ViewportContainer1/Viewport1/World func _ready(): viewport2.world_2d = viewport1.world_2d onready ノード参照は利便性のためです。作業中に随時使用します。 $と入力すると、Godotは自動的にノードパスを提案してくれるので、手動でタイプする必要はありません。 また、シーンツリーからノードをスクリプトエディタへドラッグすれば、そのノードのパスを取得できます。\n現在シーンを実行すると、両方のビューポートでレンダリングされた世界が表示されます。ただし、どちらのカメラも移動していないため、表示されるのは世界のごく一部に過ぎません。\nカメラのセットアップ方法 以下のスクリプトを各カメラに適用してください。\nextends Camera2D var target = null func _physics_process(delta): if target: position = target.position 現在、各カメラにターゲットを割り当て、そのノードの座標に従うように設定できます。 以下のMainスクリプトで実装します。\nfunc _ready(): viewport2.world_2d = viewport1.world_2d camera1.target = world.get_node(\"Player_1\") camera2.target = world.get_node(\"Player_2\") 現在シーンを実行すると、各プレイヤーは自分のビューポートの中央に配置され、分割画面の設定が正しく機能しています！\nヒント カメラの Drag Margin プロパティを無効にすると、見た目が一番良くなります。\nカメラの制限 次に、プレイヤーカメラがマップの表示範囲外にスクロールしないように制限を追加してください。この関数をメインスクリプトに追加し、_ready() で呼び出してください。\nfunc set_camera_limits(): var map_limits = world.get_used_rect() var map_cellsize = world.cell_size for cam in [camera1, camera2]: cam.limit_left = map_limits.position.x * map_cellsize.x cam.limit_right = map_limits.end.x * map_cellsize.x cam.limit_top = map_limits.position.y * map_cellsize.y cam.limit_bottom = map_limits.end.y * map_cellsize.y ミニマップ もう一つ便利な機能を追加してください。マップ全体を見渡せるミニマップです。プレイヤーが現在地を把握しやすくなります。\nさらにもう 1 つ の ViewportContainer が必要です。今回は Main の子要素として配置します。この場合、 _Stretch_は使用しません*。代わりに Viewport を追加し、_Size_を (340, 200) に設定します その後、 の Camera2D 要素を追加してください。表示画面中央に配置するため、Camera2D の Position を (512, 300) に設定しましょう。表示範囲を拡大するには、Zoom を (9, 9) に設定してください。このカメラについても忘れずに Current を選択してください。\n_ready()関数内では、ミニマップが他の2つのビューポートと同じワールドを使用するように設定します。\n$Minimap/Viewport.world_2d = viewport1.world_2d 「レイアウト」メニューを使用してMinimapコンテナを「中央下部」に配置してください。実際に見てみてください。\nエッジ周りのグレーゾーンを解消が必要です。正確なズームレベルを特定して希望のミニマップサイズに合わせることもできますが、代わりにViewport設定の_Transparent Bg(透過背景)_をチェックします。これで非地図領域が見えなくなり、ミニマップがメインビューポートの上に直接浮かんで表示されるようになります。\nまとめ ビューポートは非常に強力な機能ですが、同時に混乱を招く可能性もあります。効果的な管理方法として、ゲームロジックから完全に切り離し、表示専用として使用する方法が有効です。\n","description":"","tags":null,"title":"分割画面マルチプレイヤー","uri":"/godot_recipes/4.x/ja/2d/splitscreen_demo/index.html"},{"content":"線形補間（リニア・インターポレーション）、あるいはその略称であるlerpは、ゲーム開発の現場で頻繁に用いられる概念です。初めて耳にする方には難解で技術的に聞こえてしまうかもしれませんが、このチュートリアルを通じてご覧いただけるように、実際には理解しやすいシンプルな原理でありながら、ゲームプログラミングにおいて幅広く応用可能な強力なツールなのです。\n数値補間処理 線形補間の基本式は以下のようになります。\nfunc lerp(a, b, t): return (1 - t) * a + t * b この数式において、a と b は2つの値を表し、t は補間量を示します。通常、0（結果はa）から1（結果はb）までの範囲で指定します。この関数は、指定された比率に基づいて2つの値の間に適切な中間値を求めます。具体例を挙げると以下となります。\nx = lerp(0, 1, 0.75) # x is 0.75 x = lerp(0, 100, 0.5) # x is 50 x = lerp(10, 75, 0.3) # x is 29.5 x = lerp(30, 2, 0.75) # x is 9 この手法が「線形補間」と呼ばれる理由は、2点間の経路が直線であるためです。\nノードのプロパティをlerp()でアニメーション化できます。例えば、経過時間を希望する持続時間で割ると、0から1の間の値が得られ、これを使ってプロパティを滑らかに変化させることができます。このスクリプトでは、スプライトを開始サイズの5倍まで拡大しつつ、2秒間かけて徐々にフェードアウトさせます（modulate.aを使用して)。\nextends Sprite2D var time = 0 var duration = 2 # length of the effect func _process(delta): if time \u003c duration: time += delta modulate.a = lerp(1, 0, time / duration) scale = Vector2.ONE * lerp(1, 5, time / duration) ベクトル補間 また、ベクトル間での補間もできます。Vector2 および Vector3 はどちらも lerp() メソッドを提供しています。\n例：Node3Dノードの前方方向ベクトルと左方向ベクトルのちょうど中間に位置するベクターを取得するには。\nvar forward = -transform.basis.z var left = transform.basis.x var forward_left = forward.lerp(left, 0.5) 以下の例では、スプライトノードをマウスクリック位置に移動させています。各フレームごとにノードは目標位置まで10%ずつ近づきます。これにより、オブジェクトが近づくにつれて速度が徐々に減速する「接近」効果が得られます。\nextends Sprite2D var target func _input(event): if event is InputEventMouseButton and event.pressed: target = event.position func _process(delta): if target: position = position.lerp(target, 0.1) 補間のより高度な応用については、Tween を参照してください。\n","description":"","tags":null,"title":"補間","uri":"/godot_recipes/4.x/ja/math/interpolation/index.html"},{"content":"最終パートでは、3Dプロジェクトを開始し、3D空間のナビゲーション方法とオブジェクト作成について学びました。このセクションでは、自分で作成したまたはダウンロードした既存の3Dオブジェクトのインポート方法と、Godotのより高度な3Dノード機能の使い方をマスターしましょう。\n3Dオブジェクトのインポート方法 Blenderなどの3Dモデリングソフトに慣れている方なら、ゲーム用のモデルを作成することもできます。そうでない場合でも、特定のゲームタイプ向けに作られたオブジェクトや、複数のオブジェクトがセットになったコレクションをダウンロードできるリソースは数多くあります。個人的にオススメするフリーゲームアートの制作者の一つにKenney.nlがあります。\nチュートリアルで使用するのはKenneyの「プラットフォーマーキット」です。こちらからダウンロードできます。https://kenney.nl/assets/platformer-kit\nこのキットには、Godotの3Dスキルを習得するために使用できる豊富なオブジェクトが揃っています。以下にキットの外観サンプルをご紹介します。\nダウンロードしたキットを開くと、内部にはさまざまな形式のオブジェクトが含まれています。Godotは複数のフォーマットを使用できますが、このパックではGLTFが利用可能であるため、他の形式よりも推奨されます。GLTF formatフォルダをそのままGodotプロジェクトフォルダにコピーし、ファイル名を「platformer_kit」にリネームしてください。\n3Dファイル形式について 自分でモデルを作成する場合も、外部でダウンロードしたデータを使用する場合も、それがGodot環境で正しく読み込める形式で保存されている必要があります。Godotは以下の主要な3Dファイル形式をサポートしています。\nglTF - .gltf（テキスト形式）と.glb（バイナリ形式）の両方に対応 DAE (Collada) - 古いフォーマットながら現在もサポート継続中 OBJ (Wavefront) - こちらも旧式フォーマットですが、現代の他のフォーマットに比べると使える機能に制限があります FBX - 商用フォーマットであり、限定的な互換性しか備えていません Godotウィンドウに戻ると、フォルダのスキャンとオブジェクトのインポート処理中に進捗バーが表示されます。その中の一つをクリックして中身を確認してみます。ファイルシステムタブで、crate.glbをダブルクリックしてください。\nここでオブジェクトがシーンとしてインポートされ、ルートのが Node3D に設定され、「Scene Root」という名前になっていることを確認できます。これらを変更してください。ルートの型を RigidBody3D に、ルート名を「Crate」に設定したら、「再インポート」ボタンをクリックしてください。\n次に「crate.glb」を右クリックし、新しい継承シーンを選択します。ここでクラシックなゲームオブジェクトである箱が登場します。シーンのルートノードは、求めていた通り「Crate」という名前のRigidBody3Dとなっています。\n最後に、ボディに衝突形状を追加する必要があります。通常は 2D ゲームで行うように CollionShape3Dを追加することも可能ですが、より効率的な方法があります。\nメッシュ crate2 を選択すると、ビューポート上部に Mesh メニューが表示されます。クリックして「コリジョンシェイプを作成」から「Collision Shape Placement」で兄弟 を選択してください。Godotは自動的にメッシュに対応した衝突形状の CollionShape3D を追加してください。\nこれでオブジェクトの設定は完了です。Crateシーンを保存して、実際にどのように使用できるか見てみてください。\n3Dシーンの構築 Node3Dをルートとして新規シーンを作成します。最初に追加する子オブジェクトは、木箱を積み上げるための「地面」機能を提供するものです。“Ground\"という名前の StaticBody3Dを作成し、それに MeshInstance3Dを追加してください。メッシュ プロパティから 新規で「BoxMesh」 を選択し、クリックしてそのプロパティを開きます。サイズ を (10, 0.1, 10) に設定することで、広々とした表面を確保できます。ただし、白色一色では見た目がやや単調になるため、もう少し工夫を加えます。\nメッシュのプロパティには、さらにマテリアル設定があります。マテリアルとは、オブジェクトの外観を定義するための要素です。まず「New StandardMaterial3D」を選択し、クリックすると詳細なプロパティ一覧が表示されます。メッシュの色を設定するにはアルベド/カラープロパティを指定が必要です。茶色や濃緑色など、お好みの色を選択してください。\nクレートを追加すると、メッシュを貫通して落ちてしまうため、衝突形状も設定する必要があります。Groundに CollisionShape3Dを追加し、Shapeから新規で BoxShape3D を選択します。衝突ボックスのサイズは、メッシュと同じ大きさに設定してください。\n次に、シーンに木箱を配置し、大まかな山積み状に並べます。Cameraを追加して、木箱がよく見える位置に設置してください。シーンを実行して、木箱が転がり落ちる様子を観察します！\nシーンが暗く表示される理由は、ライティングが全く設定されていないからです。デフォルトでは、Godotはエディターのビューポートと同様に、シーンに任意のライティングや環境設定を追加しません。これは独自のライティングをカスタマイズしたい場合には便利ですが、このような簡易サンプルシーンの場合は、ショートカットが使えます。\nライティング 3D環境には多種多様なライティング効果を作成可能なライトノードが複数用意されています。まずはDirectionalLight3Dから解説を始めてください。ただし手動で追加するのではなく、エディターウィンドウで使用されているのと同じライトを使用する方法を紹介します。ビューポート上部には、プレビューライティングとプレビュー環境を制御する2つのアイコンがあります。これらの横にある三点リーダーをクリックすると、その設定内容を確認できます。\n「太陽をシーンに追加」ボタンをクリックしてください。Godotはシーンに DirectionalLight3D ノードを追加してください。次に「環境をシーンに追加」をクリックすると、プレビュー用の空に対しても同様に処理が行われ、WorldEnvironment ノードが追加されます。\nシーンをもう一度実行すると、箱が落下する様子を確認できるようになります。\nカメラの回転操作 シーン内でカメラをゆっくりと回転させながら周回させることで、よりダイナミックな動きを演出します。ルートノードを選択し、Node3Dコンポーネントを追加してください。このコンポーネントは座標 (0, 0, 0) に配置され、「CameraHub」と命名してください。シーンツリーでカメラをドラッグし、この新しいノードの子オブジェクトとして設定します。これで、CameraHubが y 軸を中心に回転すると、それに連動してカメラも一緒に移動するように動作します。\nルートノードにスクリプトを追加し、以下の内容を追加してください。\nextends Node3D func _process(delta): $CameraHub.rotate_y(0.6 * delta) シーンを実行して結果を確認してください。\nまとめ このチュートリアルでは、外部ソースから3Dオブジェクトをインポートする方法と、それらを統合してシンプルなシーンを構築する方法を学びました。さらに、ライティングと移動カメラの設定についても詳しく確認しました。\n次のパートでは、より複雑なシーンを構築する方法と、プレイヤーが操作するキャラクターを組み込む方法についてご紹介します。\n","description":"","tags":null,"title":"3Dオブジェクトのインポート","uri":"/godot_recipes/4.x/ja/g101/3d/101_3d_02/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 キネマティックカーで高速走行時に「レール上を走っている」ような感覚が気にいりません。ドリフトやトラクション損失を可能にするため、ある程度の「スリップ」特性が欲しいです。\n解決策 車がドリフト走行している場合、進行方向（向いている向き）と速度ベクトル（移動方向）は必ずしも一致しません。ハンドルを切ると車は旋回しますが、速度ベクトルはすぐに追従してくれません。代わりに lerp() 関数を使用して、徐々に目標方向への速度を調整します。\n以下の新しい変数をcar_base.gdに追加してください。\n@export var slip_speed = 9.0 @export var traction_slow = 0.75 @export var traction_fast = 0.02 var drifting = false slip_speedは、車のトラクションが失われる前に到達すべき速度を指定します。この値は、車の他のパラメーターに合わせて調整が必要となります。\ntraction_slow および traction_fast は、slip_speed 以下またはそれ以上におけるトラクション特性を表し、値は 0～1 の範囲で設定されます。数値が小さいほど車両は「滑りやすい」感覚が強くなります。これらを 1 に設定すると、「レール上を走る」ように全く滑らない状態になります。\ndrifting はドリフト状態を追跡するためのブール変数です。\n次に、car_base.gd ファイルの calculate_steering() 関数内で、new_heading を計算した直後にこのコードを追加してください。\n# traction if not drifting and velocity.length() \u003e slip_speed: drifting = true if drifting and velocity.length() \u003c slip_speed and steer_angle == 0: drifting = false var traction = traction_fast if drifting else traction_slow このコードでは状況に応じてdrifting(ドリフト値)を設定し、その後どのトラクション値を使用するかを決定します。\n最後の手順は、速度を新しい進行方向に合わせて補間することです。この行を変更してください。\nvelocity = new_heading * velocity.length() をこれに：\nvelocity = lerp(velocity, new_heading * velocity.length(), traction) まとめ この段階では、車の挙動を幅広く調整できる多数のパラメーターがあります。求める運転スタイルによっては、ここで示した数値とは大きく異なる設定が必要になる場合もあります。\nさらに学びたい方のために、フォローアップレシピで扱うトピックをご紹介します。\n車両カメラ制御およびカメラ操作 AI／NPCの制御（ステアリング、障害物回避、走行ライン追従） 斜面やスロープ地形への対応機能 関連レシピ 運動車：基本モデル 2D: 車両のステアリング制御レシピ 入力アクション設定 3D: CharacterBody3Dの移動機能 この動画が気に入ったら？ ","description":"","tags":null,"title":"3Dで自動車を作ろう：牽引とドリフト","uri":"/godot_recipes/4.x/ja/3d/kinematic_car/car_traction/index.html"},{"content":"課題 ファーストパーソン・シューティングゲーム（FPS）用のキャラクターを作成してください。\n解決策 まず、CharacterBody3D ノードから始め、次に CollisionShape3D を追加してください。この場合、最も一般的な選択肢は CapsuleShape3D 衝突形状です。ワールドの設定によっては、ここに他の形状も追加できますが、この例では基本に忠実に進めます。\nサイズはすべて初期設定値のままにします（カプセルの高さは2メートルとなります）。地面と底面を揃えるため、高さを1.0m移動させてください。\n次に、ボディの子要素として Camera3D を追加し、約 1.6m 持ち上げてください。\nキャラクターの身体はどこにある？ この例では、「身体がない」状態、つまりプレイヤー用の表示メッシュを追加しないケースを考えます。環境によっては、プレイヤーの身体を表示する必要があるかどうかは異なるでしょう。\nスクリプトをbodyにアタッチし、まずプロパティを定義することから始めてください。\nextends CharacterBody3D var gravity = ProjectSettings.get_setting(\"physics/3d/default_gravity\") var speed = 5 var jump_speed = 5 var mouse_sensitivity = 0.002 _physics_process()関数は移動処理を担当する箇所です。注意すべき点として、Input.get_vector()関数は前進/後退/左右キーの組み合わせに基づいて2次元ベクトルを返します。このベクトルを利用して、ボディの速度におけるxおよびz成分を設定することになります（y方向は重力によって自動的に処理されるため）。このベクトルにボディのbasisを掛けることで、回転を考慮に入れつつ適切な方向に移動させることができます。つまり、前方は常に『ボディ自体』の前進ベクトルを指すようにするわけです。\nfunc _physics_process(delta): velocity.y += -gravity * delta var input = Input.get_vector(\"left\", \"right\", \"forward\", \"back\") var movement_dir = transform.basis * Vector3(input.x, 0, input.y) velocity.x = movement_dir.x * speed velocity.z = movement_dir.z * speed move_and_slide() if is_on_floor() and Input.is_action_just_pressed(\"jump\"): velocity.y = jump_speed 入力マッピングに「W」「A」「S」「D」（標準設定）またはコントローラーの軸（好みに応じて選択可能）を使用して、適切な入力アクションを必ず追加してください。\n以下の手順で「World」シーンにプレイヤーを追加してください。床にStaticBody3Dノードを、壁用に複数のノードを作成済みとします。\n移動しようとすると、前後・左右に動けるものの、回転はできないことがわかります。次はこの点を処理していきます。\n3D空間におけるマウス操作制御 まず、マウスを動かすのと同じ方向にプレイヤーを左／右に回転させる必要があります。マウス入力は画面座標系で2次元的に表現されるため、マウスの水平方向（x）の動きを、垂直軸であるyを軸にしたプレイヤー本体の回転に変換する必要があります。先ほど定義したmouse_sensitivityプロパティを使えば、マウス移動量がどれだけ回転角度に相当するかを調整できます。\nfunc _input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) コードをもう一度実行してみると、マウスで回転操作ができるようになっていることが確認できるはずです。ただし、マウスカーソルがゲームウィンドウの外にはみ出してしまう場合があります。このタイミングで、マウスをキャプチャするコードを追加すると良いでしょう。詳細については入力：マウスのキャプチャをご覧ください。\n更新されたコードは次のようになります。\nfunc _input(event): if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: rotate_y(-event.relative.x * mouse_sensitivity) 最後に、上下移動にはマウスのyモーションを使ってカメラを傾けます。完全に逆さまになるのは避けたいので、回転値はclamp()で70度という合理的な範囲に制限します。\nfunc _input(event): if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: rotate_y(-event.relative.x * mouse_sensitivity) $Camera3D.rotate_x(-event.relative.y * mouse_sensitivity) $Camera3D.rotation.x = clampf($Camera3D.rotation.x, -deg_to_rad(70), deg_to_rad(70)) 武器の所持について FPSキャラクターには通常、前面に位置した武器の3Dメッシュが用意されています。これをセットアップするのは、Godotエディターの便利な機能を使えば簡単に行えます。\n武器モデルをCamera3Dの子要素として追加してください。その後、エディタービューメニューで「2つのビューポート」を選択し、そのうち1つをカメラプレビュー用に設定してください。これで、武器を自由に移動させながら、プレイヤー視点でどのように見えるかを簡単に確認できるようになります。\n個性を加えるには、AnimationPlayer を使用して武器の位置をプレイヤーの移動に合わせて左右にアニメーションさせる方法がオススメです。\n関連レシピ 入力：マウスキャプチャーの使用方法 プロジェクトのダウンロード プロジェクトコードはこちらからダウンロードしてください。https://github.com/godotrecipes/basic_fps\n","description":"","tags":[],"title":"FPSキャラクター","uri":"/godot_recipes/4.x/ja/3d/basic_fps/index.html"},{"content":" GDScript Godot の組み込みスクリプト言語である GDScript は、その構文が Python をベースにしているため、Python に慣れている方ならすぐに使いこなすことができるでしょう。この章では、この言語の基本概念と動作原理について紹介します。\nGodot 4.0 へのアップデート準備中 【注 : この文章はオリジナル版の説明です】現在は、Godot 4.0対応版のGodot 101を開発中です。それまでの間、新規学習者の方にはGodot 3.xシリーズの使用をオススメします。このバージョンにはより多くの公式リソースや学習教材が揃っています。\n目次 ： 入門 ","description":"","tags":null,"title":"GDScript入門","uri":"/godot_recipes/4.x/ja/g101/gdscript/index.html"},{"content":"プロジェクトマネージャー 「プロジェクトマネージャー」は、Godotを起動したときに最初に表示される画面です。\nこのウィンドウでは、Godotプロジェクトの一覧を確認できます。既存のプロジェクトを選択して「実行」をクリックすればゲームをプレイ可能、または「編集」をクリックしてGodotエディターで直接作業することもできます。まだプロジェクトを作成していない場合は、まず「＋作成」ボタンをクリックすることから始めてください。\nここにプロジェクト名を入力し、保存用のフォルダを作成できます。\nメモ Godot プロジェクトはすべて独自のフォルダ内に格納されます。これにより 移動や共有、バックアップが容易になる、などの利点があります。よって、すべてのプロジェクトファイル（画像、サウンドなど）は必ずプロジェクトフォルダ内に収める必要があります。\nプロジェクトに名前を付ける際は、その内容を適切に表現できる名称を選んでください。「新規ゲームプロジェクト #23」のような曖昧な命名では、後でそのプロジェクトの詳細を思い出すのが困難になります。互換性についても考慮が必要です。OSによっては大文字小文字を区別する場合としない場合があるため、これを誤ると別のコンピュータに移動したり共有した際に問題が生じる可能性があります。このような理由から、多くのプログラマーは標準化された命名規則を採用しています。(例、スペースを使わず、単語区切りにはアンダースコア(’_’)を使用する)。\nこの新規プロジェクトを「getting_started」と命名します。その名前を入力し、フォルダを作成をクリックしてから、作成 \u0026 編集をクリックしてください。\n現在、Godotエディターウィンドウを開いています。これはGodotで作業する際に最も見る画面です。エディターはセクションに分かれて構成されています。\nビューポート: 開発中のゲーム画面が表示される領域です。 ワークスペース: 上部中央に配置されており、2D/3D/スクリプト各作業環境を簡単に切り替えられます。初期状態では3Dモードが選択されています。 プレイテストボタン: このボタンを使用すると、開発中のゲームを起動して操作しながらデバッグできます。 ドック/タブパネル: 左右両側にある複数のドックエリアでは、ゲーム内アイテムを表示し、各種プロパティを設定できます。 ボトムパネル: ここには各ツールに関連したコンテキスト固有情報が表示されます。特に重要なのは出力パネルで、ゲーム実行時にエラーメッセージや動作状況を確認できます。 プロジェクト設定 Godot のウィンドウの主要コンポーネントとその機能について触れたところで、次はプロジェクト設定について少しお話しします。新規プロジェクトを開始する際、まずやるべきことは環境を正しくセットアップすることです。\nそれでは、メニューのプロジェクトをクリックして、プロジェクト設定を選択してください。\nこれはプロジェクト設定ウィンドウです。左側にはカテゴリの一覧が表示されます。ほとんどのプロジェクトでは初期設定のままで問題ありませんが、特別なニーズがある場合を除いて、変更する必要はありません。まず、 アプリ/設定 項目について説明します。\nここで、ゲームのタイトルを設定したり、「メインシーン」（詳細については後ほど解説）を指定したり、アイコンを変更したりできます。\n次に、表示設定セクションを確認してください。ここではゲームの画面表示を設定します。幅 と 高さ パラメーターではゲームウィンドウのサイズを指定できます。例えばモバイル向けゲームを開発する場合、対象デバイスの解像度とアスペクト比に合わせて設定してください。その他にもスケーリング処理や縦横比変更、フルスクリーンモードなどの各種設定ができます。現時点ではデフォルトサイズのままにしておきますが、後ほど異なるデバイス環境で動作させるための調整方法について詳しく説明します。\n画面上部にはタブも複数あります。これまで 一般 タブについて説明してきましたが、ここで簡単に インプットマップ についても触れておきます。この機能では、キーボード操作、ゲームパッド、マウスなど、さまざまな入力デバイスに対する個別のアクションを定義できます。実際のゲームプレイ時には、特定のキーやボタンが押されたかどうかを気にする必要はありません。これはプレイヤー入力を扱う上で非常に強力で柔軟な方法です。\nまた、多言語対応を予定している場合、ローカライゼーション機能も使えます。後で詳しく説明する自動機構やプラグインシステムもあります。Godotコミュニティが開発した様々な便利なプラグインをダウンロード・追加することで、さらに多くの機能やツールなどを拡張できます。\nプロジェクト設定ウィンドウは後で戻ってきます。今のところ閉じておき、次のステップに進む準備をします。次はノード操作についてです。\n","description":"","tags":null,"title":"Godot エディター：操作方法の基本ガイド","uri":"/godot_recipes/4.x/ja/g101/start/101_02/index.html"},{"content":"課題 対象物を観察するため、リジッドに滑らかな回転動作をしたい。\n解決策 RigidBody2D の操作は少し複雑です。Godot の物理エンジンで制御されるため、直接移動させるのではなく、力を加える必要があります。リジッドボディを扱う前に、RigidBody2D API ドキュメント を読むことを強くオススメします。\n物体を回転させるには、回転力であるトルクを加える必要があります。一度物体が回転し始めたら、最終回転に近づくにつれてトルクを小さくしていきたいものです。\nこれはまさに「内積」が活躍する場面です。その符号からターゲットの位置が左側か右側かを判断でき、絶対値からは我々が向いている方向とターゲット方向との距離を把握できます。\nヒント ドット積について簡単に復習するには、ベクトル: 内積と外積の使い方をご覧ください。\nextends RigidBody2D var angular_force = 50000 var target = position + Vector2.RIGHT func _physics_process(delta): var dir = transform.y.dot(position.direction_to(target)) constant_torque = dir * angular_force ここでtransform.yを使用している理由について疑問に思われるかもしれません。というのも、transform.xはボディの前方ベクトルを表すからです。もしtransform.xを使用していれば、ドット積が最大となるのはボディが完全にターゲット方向を向いている時になりますが、その時点でトルク値をゼロにしたいです。一方、transform.yを使用することで、ターゲット方向に完全に揃っていない状態ほどトルク値が増加するようになります。\nリジッドボディを完全にスキップする リジッドボディを一切回転させないことで、これらの問題をすべて回避できます！代わりに、子スプライトのrotationプロパティをターゲット方向に合わせるように変更してください。lerp()関数やTweenを使用することで、滑らかにアニメーションさせることが可能になります。\n多くの場合、これは有効な解決策となります。覚えておいてください。基底体の向きは、付属するスプライトと必ずしも揃える必要はないのです！\n関連レシピ ベクトル演算：内積と外積の活用 RigidBody2D：目標位置への移動方法 ","description":"","tags":null,"title":"RigidBody2D で対象物を見る","uri":"/godot_recipes/4.x/ja/physics/smooth_rigid_rotate/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n前回のパートでは、ゲームの中核をなすジャンパーオブジェクトとサークルオブジェクトを作成しました。次に、プレイヤーがミスするまで連続して出現するサークルの進行システムを追加する必要があります。\nメインシーンの拡張 メイン・ノードにさらにノードを追加してください。\nMarker2D (“StartPosition”)\nこのゲームの開始位置をマークします。画面中央下部付近に置いてください。\nカメラ 2D\nカメラはプレイヤーの動きに合わせて追従します。\nカメラの設定も行います。 オフセット を (0, -200) に設定してください。これにより、前方の世界をより広く確認できるようになります。また、カレントモードは「オン」に設定してください。\nメインシーンのスクリプト化 手動で作成したジャンパーと円のインスタンスを削除します。今後はコード内で追加するようにします。\n以下をJumper.gdに追加してください。\nsignal captured ジャンパーが円に接触した時に、このシグナルを発生させます。\nfunc _on_Jumper_area_entered(area): target = area velocity = Vector2.ZERO emit_signal(\"captured\", area) また、円の init() 関数を修正して、位置も受け取れるようにしてください。\nfunc init(_position, _radius=radius): position = _position それでは、Main シーンにスクリプトを追加してください。\nextends Node var Circle = preload(\"res://objects/Circle.tscn\") var Jumper = preload(\"res://objects/Jumper.tscn\") var player 需要が発生した際にインスタンス化できるよう、両方のオブジェクトに対する参照が必要となります。\nfunc _ready(): randomize() new_game() これは一時的なものです。後で新規ゲーム機能を呼び出すスタートボタンを備えたUIを実装する予定です。\nfunc new_game(): $Camera2D.position = $StartPosition.position player = Jumper.instantiate() player.position = $StartPosition.position add_child(player) player.connect(\"captured\", self, \"_on_Jumper_captured\") spawn_circle($StartPosition.position) new_game()関数は、プレイヤーと円を開始位置にスポーンさせ、カメラを設定することでゲームを初期化します。\nfunc spawn_circle(_position=null): var c = Circle.instantiate() if !_position: var x = rand_range(-150, 150) var y = rand_range(-500, -400) c.position = player.target.position + Vector2(x, y) add_child(c) c.init(_position) こちらがspawn_circle()関数です。位置を指定すればその位置にオブジェクトを配置し、指定しない場合は現在のターゲットから一定距離離れた場所にランダム配置されます。これらの数値は暫定値であり、ゲームプレイシステムが完全に実装された後に微調整する予定です。\nfunc _on_Jumper_captured(object): $Camera2D.position = object.position call_deferred(\"spawn_circle\") 最後に、ジャンパーのcapturedシグナルを処理する関数が必要です。この関数ではカメラを新しい円に移動させ、別のインスタンスを生成します。なお、この関数は物理演算処理中に呼び出されるため、シーンツリーに追加しようとするとエラーが発生します。call_deferred() を使うことで、エンジンが安全に実行できるタイミングが来るまでその関数の実行を遅延させることができます。\n試してみてください。円から円へジャンプできるはずです。いくつ成功しましたか？\nカメラが次のサークルに移動するときに「瞬間移動」してしまうのは不自然です。これは、カメラの「Smoothing」を有効にすることで改善できます。「Smoothing/Speed」は、新しい位置への補間の速さを制御します。5から10の間で試してみてください。\n調整項目 また、円オブジェクトに衝突してもその場で回転が開始されない点も違和感があります。以下のコードをジャンパー用の_on_Jumper_area_entered()関数に追加してください。\ntarget.get_node(\"Pivot\").rotation = (position - target.position).angle() Circleのinit()メソッドにもこれを追加しておきます。\nrotation_speed *= pow(-1, randi() % 2) この機能はランダムに回転速度の向きを正または負に切り替えるため、常に同じ方向に周回するわけではありません。\nトレイル これらのノードをジャンパに追加してください。\nNode (“Trail”) Line2D (“Points”) これを使って、プレイヤーの後ろに流れるトレイルを作成します。後でより視覚的に魅力的なものにしますが、今のところは単純なグラデーションで進めてください。「Fill」部分で新しいグラデーションを追加し、透明からお好みの色に変更してください。\nジャンパのスクリプトに、以下を追加してください。\n@onready var trail = $Trail/Points var trail_length = 25 そして _physics_process() 内では：\nif trail.points.size() \u003e trail_length: trail.remove_point(0) trail.add_point(position) 画像 / GIF\nサークルアニメーション 最後に、円に視覚効果を追加してください。まず、プレイヤーがジャンプして離れた時に円が消えるエフェクトを実装します。さらに、円に触れた際のキャプチャーエフェクトも追加してください。\nCircle ノードに AnimationPlayer を追加してください。\n「インプロージョン」アニメーション 新しいアニメーション「implode」を追加してください。長さを 0.4 に設定し、ルートノードである Area2D の 2 つのプロパティにキーフレームを設定してください。スケールは (1, 1) に、モジュレートはデフォルト値 (1, 1, 1, 1) に。その後、スクラブバーを最後まで移動させ、値として (0.1, 0.1) と (1, 1, 1, 0) を設定します（これは色のアルファ値です）。\nアニメーションのキャプチャ キャプチャアニメーションはもう少し複雑です。スプライトを複製してSpriteEffectという名前に変更してください。その［表示］プロパティはオフに設定します。この二つ目のリングがメインサークルに向かってズームインするアニメーションを作成します。\n以下の機能をサークルスクリプトに追加する必要があります。\nfunc capture(): $AnimationPlayer.play(\"capture\") func implode(): if !$AnimationPlayer.is_playing(): $AnimationPlayer.play(\"implode\") await $AnimationPlayer.animation_finished queue_free() そしてJumper.gd内では、ジャンプ関数は以下のようになります。\nfunc jump(): target.implode() target = null velocity = transform.x * jump_speed メイン画面では、「キャプチャ」メソッドが実際のキャプチャ処理を実行します。\nfunc _on_Jumper_captured(object): $Camera2D.position = object.position object.capture() call_deferred(\"spawn_circle\") GIF\nGitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\n動画で見る ","description":"","tags":null,"title":"サークルをスポーンさせる","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_02/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 「無効なノード参照」、これはGodotヘルプチャンネルで最も頻繁に報告される問題の一つです。ほとんどの場合、以下のようなエラーメッセージとして表示されます。\nInvalid get index ‘position’ (on base: ’null instance’).\n解決策 問題の核心は「nullインスタンス」部分にあり、これがGodot初心者にとって最も混乱を招く要因となっています。\nこの問題を回避するには、「ノードパス」の仕組みを理解することが重要です。\nノードパスの理解 シーンツリーはノードで構成されており、これらは親子関係によって接続されています。ノードパスとは、このツリー構造を辿りながらあるノードから別のノードへ移動する際に通る経路のことです。\n例として、シンプルな「プレイヤー」シーンを考えてみます。\nこのシーンのスクリプトは Player ノードに実装されています。もしスクリプトが AnimatedSprite ノードに対して play() メソッドを呼び出す必要がある場合、そのノードへの参照が必要となります。\nget_node(\"AnimatedSprite\").play() get_node() 関数の引数は、対象ノードへのパスを表す文字列です。ここではスクリプト実行中のノードの子要素を指定します。指定したパスが無効な場合、厄介な null instance エラーが発生するほか（さらに「ノードが見つかりませんでした」というメッセージも表示されます）。\nノード参照を get_node() で取得する状況は非常に頻繁にあるため、GDScript にはそのためのショートカットが用意されています。\n$AnimatedSprite.play() 情報 get_node() 関数は、対象ノードへの 参照 を返します。\nではここで、より複雑なシーンツリーを見てみてください。\nもし Main スクリプトが ScoreLabel にアクセスする必要がある場合、以下のパスを使用してアクセスできます。\nget_node(\"HUD/ScoreLabel\").text = \"0\" # or using the shortcut: $HUD/ScoreLabel.text = \"0\" ヒント ドル記号($)を使用した場合、Godotエディタがパスを自動補完します。またシーンタブでノードを右クリックし、「ノードのパスをコピー」を選択する方法もあります。\nノードへのアクセス先がツリー階層の上位にある場合はどうすればよいでしょうか？その場合は get_parent() 関数を使うか、パス指定に \"../\" を使用することで親ノードを参照できます。上記の例で、ScoreLabel から Player ノードを取得するには。\nget_node(\"../../Player\") 分解して説明します。パス「\"../../Player\"」は、次のように解釈されます。\nまず一つ上の階層にあるノード（HUD）を取得します さらにもう一段階上の階層に移動し（Main）、その中から子ノードであるPlayerを特定します ヒント 見覚えがありますか？ ノードパスの仕組みは、オペレーティングシステムのディレクトリパスと完全に同じです。スラッシュ/は 親と子 の関係を示し、..は「1つ上の階層に移動」の意味です。\n相対パスと絶対パスの違い 上記の例はすべて相対パスを使用しています。これは現在のノードを起点として、目的地までの経路をたどる形式です。ノードへのパスは絶対パス形式で指定することも可能で、この場合シーンのルートノードを基点とします。\n例えば、プレイヤーノードが絶対パスで指定されている場合は。\nget_node(\"/root/Main/Player\") /root は get_tree().root を介してもアクセスできますが、これはシーンのルートノードではありません。これはデフォルトで常に SceneTree に存在するビューポートノードです。\n注意事項 上記の例は問題なく動作しますが、後々問題を引き起こす可能性のある注意点があります。以下のような状況を想像してみてください。Playerノードにはhealthプロパティがあり、これをUI内のどこかにあるHealthBarノードに表示したいとします。プレイヤースクリプトに次のように書いたとします。\nfunc take_damage(amount): health -= amount get_node(\"../Main/UI/HealthBar\").text = str(health) 初期段階では問題なく機能するかもしれませんが、これは非常に「脆弱」な方式であり、簡単に破綻する可能性があることを意味します。この種の構成には主に2つの重大な問題があります。\nプレイヤーシーン単体でのテストは不可能です。プレイヤーシーンを単独で実行した場合や、UIを持たないテストシーンで使用した場合、get_node() 行が原因でクラッシュが発生します。 UIの変更はできません。UIのレイアウトを変更するか設計を見直すことにした場合は、パスが無効になるため、必ず修正してください。 この理由から、シーンツリーの 上位 方向へノードパスを指定する操作は避けるべきです。前述の状況では、プレイヤーのHPが変化した時にシグナルを発行するように変更すれば、UIコンポーネントはそのシグナルを受け取って自身の状態を更新できます。この方法なら、ゲームのソースコードに影響を与えることなくノードを自由に再配置・分離することが可能になります。\nまとめ ノードパスの使い方をマスターすれば、必要なノードを簡単に参照できるようになります。慣れればnull instanceエラーメッセージに悩まされることもなくなります。\n","description":"","tags":null,"title":"ノードパスを理解しよう","uri":"/godot_recipes/4.x/ja/basics/getting_nodes/index.html"},{"content":" ノードを知る 「ノードを知る」シリーズでは、Godotの単一ノードを詳細に解説します。その仕組みを理解するとともに、実際の使用例も紹介します。\n目次 ： Label Path2D と PathFollow2D RayCast2D ","description":"","tags":null,"title":"ノードを知る","uri":"/godot_recipes/4.x/ja/kyn/index.html"},{"content":" 情報 本記事の元ネタとなったGodot Discordの@TheDurielによる原型図に心から感謝します。この資料は保存しておき、必要に応じて参照できる状態にしておくことをオススメします。\n課題 プロジェクトが複雑化してきました。複数のシーン、インスタンス、そして膨大な数のノードが存在しています。以下のようなコードが沢山ありませんか。\nget_node(\"../../SomeNode/SomeOtherNode\") get_parent().get_parent().get_node(\"SomeNode\") get_tree().get_root().get_node(\"SomeNode/SomeOtherNode\") このようにノード参照すると、すぐに問題が発生することに気づくでしょう。シーンツリーの何かを変更すると、これらの参照がすべて無効になる可能性があるからです。\nもっと良い方法があります。ノード間やシーン間のやり取りをシンプルにしていきましょう。\n解決策 基本的に、ノードはその子ノードを管理するべきです。逆の関係(子ノードが親ノードを管理する)は避ける必要があります。get_parent()やget_node(\"..\")を使用している場合、すでに問題が発生し始めています。このようなノードパスは 脆弱 であり、簡単に壊れてしまう性質があります。この構成には主に3つの重大な問題点があります。\nシーンを単独でテストすることはできません。そのシーン単体、あるいは厳密に同一のノード構成を持たないテストシーンで実行した場合、get_node() メソッドがクラッシュを引き起こします。\n変更は簡単にはできません。ツリーの構造を変更する場合、パスが無効になる可能性があります。\nノードの_ready()が呼ばれる順番は、子ノード優先、親ノード後回しとなります。つまり、ノードの _ready() メソッド内で親プロパティにアクセスしようとすると失敗するケースがあります。これは親ノードがまだ準備中(_ready()が呼ばれていない)状態であるためです。\nヒント ノードがツリー構造にどのように追加され、準備完了状態になるかについては、ツリー順序を理解する を参照してください。\n一般的に、ノードやシーンはゲーム内の任意の場所でインスタンス化可能であるべきであり、その親オブジェクトがどのようなものになるかについて一切仮定すべきではありません。\nこのチュートリアルでは後ほど詳細な例を紹介しますが、現時点でのノード間通信における「基本原則」は以下の通りです。\n「下には呼び出しで」「上にはシグナルで」\nノードが子要素を呼び出している場合（つまりツリー構造を「下方向に」移動している場合）には、get_node() メソッドを使用するのが適切です。\nノードが「ツリー構造の上位」とやり取りする場合、シグナルを使用する方が適切でしょう。\nシーン設定を設計する際にこのルールを念頭に置いておけば、メンテナンス性に優れ、整理されたプロジェクト構築への道筋が自然と見えてきます。また、問題を引き起こす煩雑なノードパスの使用も避けられるでしょう。\nそれでは、これらの戦略を具体例とともに見ていきます。\n1. get_node() を使用する方法 get_node() は、指定されたパスを使用してシーンツリーを辿り、指定した名前のノードを検索します。\nヒント ノードパスについてより詳しく知りたい場合は、ノードパスの理解を参照してください。\nget_node() の使用例 以下の一般的な設定例について考えてみます。\nPlayer ノード内のスクリプトでは、プレイヤーの移動状況に応じて、AnimatedSprite2Dにどのアニメーションを再生すべきか通知する必要があります。このようなケースでは get_node() が適しています。\nextends CharacterBody2D func _process(delta): if speed \u003e 0: get_node(\"AnimatedSprite2D\").play(\"run\") else: get_node(\"AnimatedSprite2D\").play(\"idle\") ヒント GDScriptでは、get_node()の省略形として$を使用できます。代わりに$AnimatedSprite2Dと記述してください。\nより良い方法 このアプローチの欠点は、ノードパスを明示的に指定する必要があり、後でそのパスが変更された場合、コードも修正が必要になる点です。代わりに、@export 機能を使って直接ノードを選択することもできます。\nextends CharacterBody2D @export var animation : AnimatedSprite2D func _process(delta): if speed \u003e 0: animation.play(\"run\") else: animation.play(\"idle\") この方法では、ノードを選択することで、インスペクター上で変数の値を直接割り当てることができます。\n2. シグナルの活用方法 シグナルは、ツリーで上位に位置するノード、または同じ階層にあるノードに対して関数を呼び出すために使いましょう。\nシグナルの接続はエディター内で行えます（通常はゲーム開始前に存在するノードに対して）、またはコード内で行うこともできます（実行時にインスタンス化するノードの場合）。シグナル接続の構文は以下の通りです。\nsignal_name.connect(target_node.target_function)\nこの問題を見ると、「上位に位置するノード、または同じ階層にあるノードに接続する場合、../Siblingのようなノードパスが必要になるのでは？」と思うかもしれません。その方法でも可能ですが、先ほどのルールに反します。この組み合わせの正解は、接続を共通の親ノードを介して行うことです。\nツリー構造を 下方向 に辿るルールに従うと、シグナル発信ノードと受信ノードの共通親ノードは、定義上それらの位置を認識しており、両ノードが準備完了した時点で待機状態になります。\nシグナルの使用例 シグナルの最も一般的な使用例は、UI（ユーザーインターフェース）の更新です。プレイヤーのhealth変数が変化した際、対応するLabel（ラベル表示）やProgressBar（進捗バー）をリアルタイムに更新する必要があります。ただし、UIノードは通常プレイヤーオブジェクトから完全に分離されています（その方が設計上適切です）。プレイヤー側は、これらのノードがどこにあるのか、どのようにしてアクセスするのかを知る手段を一切持っていないはずです。\n以下に例となる設定を示します。\n注：UIはインスタンス化されたシーンであり、実際には含まれるノードを表示しているに過ぎません。ここでよく見かけるのがget_node(\"../UI/VBoxContainer/HBoxContainer/Label).text = str(health)のようなコードで、これは避けるべき実装方法です。\n代わりに、プレイヤーがHPを増減させるたびに、health_changedシグナルが発火します。これをUIのupdate_health()関数に送信する必要があり、この関数ではLabelの表示値を設定する処理を行います。Playerスクリプトでは、プレイヤーのHPが変更されるたびに以下のコードを使用しています。\nhealth_changed.emit(health) スクリプトUIには以下が含まれています。\n@onready var label = $VBoxContainer/HBoxContainer/Label func update_health(value): label.text = str(value) これで signal を関数に接続するだけです。最適な接続場所は World クラスです。ここは共通の親ノードであり、両方のノードの位置を把握しているためです。\nfunc _ready(): $Player.health_changed.connect($UI.update_health) 3. グループを活用する方法 グループ化はモジュール分離のもう一つの有効な手段であり、特に類似した複数のオブジェクトで同じ処理が必要な場合に有効です。ノードはどのグループにも自由に追加でき、メンバーシップは「add_to_group()」および「remove_from_group()」で動的に変更できます。\nグループに関するよくある誤解として、「ノード参照を『含む』ような何らかのオブジェクトや配列のようなもの」という認識があります。しかし実際にはグループはタグ管理システムです。特定のタグが割り当てられている場合、そのノードは「そのグループに属している」とみなされます。SceneTreeはこのタグ情報を管理しており、get_nodes_in_group()などの関数を使って、指定したタグを持つすべてのノードを効率的に検索できます。\nグループの使い方 ギャラガ風のスペースシューティングゲームを考えてみます。このゲームでは、画面に多数の敵機がランダムに出現します。これらの敵には種類や挙動が異なる場合があります。「スマートボム」機能を追加し、これを発動すると画面内のすべての敵を一度に破壊できるようにしたいとします。この場合、グループ機能を活用することで、最小限のコードで実装ができます。\nまず、すべての敵キャラを「enemies」グループに追加してください。これはエディターで「Node」タブを使用して行えます\nスクリプト内でグループにノードを追加することもできます。\nfunc _ready(): add_to_group(\"enemies\") 敵キャラ全員が死亡時の処理（アニメーション再生、ドロップアイテムの生成など）するexplode()関数を持っていると仮定します。これで全ての敵がグループに登録されたので、スマートボム機能を以下のように実装できます。\nfunc activate_smart_bomb(): get_tree().call_group(\"enemies\", \"explode\") 4. owner を使用した場合 owner はシーン保存時に自動的に設定される Node プロパティです。このシーン内のすべてのノードに対して、その owner にはシーンのルートノードが指定されます。これにより、子シグナルをメインノードにスムーズに接続できる便利な仕組みとなっています。\nownerの使用例 複雑なUI設計では、コンテナやコントロールが階層的に深々とネストされた構造になりがちです。ユーザーが操作する要素（例：Button）はシグナルを発生させますが、これらを適切に処理するためには、UIのルートノードにあるスクリプトにシグナルを接続する必要があります。\n例としての設定例をご紹介します。\nRoot要素のCenterContainerスクリプトには以下の関数が含まれており、任意のボタンが押されたときに呼び出したいです。\nextends CenterContainer func _on_button_pressed(button_name): print(button_name, \" was pressed\") これらのボタンは Button シーンのインスタンスであり、動的にコードを実行してボタンテキストやその他のプロパティを設定できるオブジェクトを表しています。あるいは、ゲーム状態に応じてコンテナへ動的に追加・削除されるボタンがあるかもしれません。いずれにせよ、ボタンのシグナルを接続するために必要な手順は以下の通りです。\nextends Button func _ready(): pressed.connect(owner._on_button_pressed.bind(name)) ボタンをツリー内のどこに配置して(コンテナを追加した場合でも)も、CenterContainerは常にownerとして機能します。\n関連レシピ ツリーの読み取り順序について理解する ノードパスを理解しよう ","description":"","tags":null,"title":"ノード間のやりとり（良い方法）","uri":"/godot_recipes/4.x/ja/basics/node_communication/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 マウスまたはキーボード操作で、回転中も水平を保ちつつ、ターゲットを追従できるカメラコントローラーが必要です。\n解決策 以下の手順を試してみてください。Camera3Dノードを1つ取得し、X軸（ギズモ上の赤いリング）を中心にわずかに回転させた後、続いてZ軸（青いリング）を中心に少し回転させます。次に、X軸方向の回転を逆方向に反転させ、「プレビュー」ボタンをクリックしてください。カメラが斜めに傾いた状態になっていることが確認できるはずです。\nこの問題を解決するには、カメラを ジンバル に取り付ける必要があります。これは、移動時も物体の水平を保つよう設計された装置です。2つのNode3Dノードを使用することで、左右方向と上下方向の回転を個別に制御できる簡易ジンバルを作成できます。\nノードの設定は以下のようにしてください。\nNode3D: CameraGimbal Node3D: InnerGimbal Camera3D Camera3D の Transform/Position を (0, 0, 4) に設定してください。\n以下にジンバルの仕組みを説明します。外側ノードはY軸方向のみ回転可能で、内側ノードはX軸方向のみ回転できます。手動で試したい場合は、まず「ローカル空間を使用」（メニューバーのロックアイコン隣にあるキューブアイコン - ショートカットキーは「T」）に切り替えることを忘れないでください。必ず外側ノードの 緑色リング のみを、内側ノードの 赤色リング のみを操作してください。カメラノードには一切触れないよう注意してください。\n実験が完了したら、すべての回転値を0にリセットしてください。\nキーボード操作 キーボード操作から始めてください。その後、マウスを使用するオプションも追加してください。必要な操作と割り当てられた入力は以下の通りです。\nアクション名 キー \"cam_up\" Wキー \"cam_down\" Sキー \"cam_right\" Dキー \"cam_left\" Aキー \"cam_zoom_in\" マウスホイール上方向回転 \"cam_zoom_out\" マウスホイール下方向回転 以下に初期スクリプトを示します。前述の通り、各 Node3D をその局所座標系内で特定軸周りに回転させることに注意してください。\nextends Node3D var rotation_speed = PI/2 func get_input_keyboard(delta): # Rotate outer gimbal around y axis var y_rotation = Input.get_axis(\"cam_left\", \"cam_right\") rotate_object_local(Vector3.UP, y_rotation * rotation_speed * delta) # Rotate inner gimbal around local x axis var x_rotation = Input.get_axis(\"cam_up\", \"cam_down\") x_rotation = -x_rotation if invert_y else x_rotation inner.rotate_object_local(Vector3.RIGHT, x_rotation * rotation_speed * delta) func _process(delta): get_input_keyboard(delta) 以下の手順でテストシーンを作成してください。\nMeshInstance3Dを使用してテスト用シーンを作成します そのシーン内に CameraGimbal インスタンスを配置し、動作をテストします アップ/ダウンコントロールを操作すると、カメラがぐるりと一回転してしまい、最終的には上下逆さまになってしまいます。これを防止するために、回転角度に上限を設定しましょう。\nfunc _process(delta): get_input_keyboard(delta) $InnerGimbal.rotation.x = clamp($InnerGimbal.rotation.x, -1.4, -0.01) -1.4 の値を指定すると、カメラをほぼ90度上向きにしつつ、最小角度を非常に小さく設定することで地面へのクリップを防止します。他の値も自由に試してみてください。\nマウス操作 マウスとキーボードの制御を簡単に切り替えられるよう、mouse_controlというフラグを追加してください。\n# mouse properties var invert_y = false var invert_x = false var mouse_control = false var mouse_sensitivity = 0.005 func _unhandled_input(event): if mouse_control and event is InputEventMouseMotion: if event.relative.x != 0: var dir = 1 if invert_x else -1 rotate_object_local(Vector3.UP, dir * event.relative.x * mouse_sensitivity) if event.relative.y != 0: var dir = 1 if invert_y else -1 $InnerGimbal.rotate_object_local(Vector3.RIGHT, dir * event.relative.y * mouse_sensitivity) func _process(delta): if !mouse_control: get_input_keyboard(delta) このコードは、水平方向のマウス操作を外側ジンバルのY軸回転に、垂直方向の操作を内側ジンバルのX軸回転に変換する仕組みになっています。さらに、invert_x と invert_y フラグを追加し、いずれかの軸に対して動きを反転させることができるようにしました。多くのプレイヤーはどちらか一方の方式を好むため、両方のオプションを用意しておくのがベストです。\nまた、_process() 関数では、マウス操作時のキーボード入力を無効化しています。\nマウスを急激に動かすと、上下移動に問題が生じることに気付くかもしれません。event.relative.y の値が大きい場合、制限された値の反対側に「スキップ」する現象が発生します。この問題を解決するには、垂直方向のマウス移動を合理的な範囲に制限すればよいでしょう。上記コードの y 部分を以下のように変更してください。\nif event.relative.y != 0: var dir = 1 if invert_y else -1 var y_rotation = clamp(event.relative.y, -30, 30) $InnerGimbal.rotate_object_local(Vector3.RIGHT, dir * y_rotation * mouse_sensitivity) このプロジェクトでは、ゲームプレイ中にマウス入力を取得する必要も出てくるでしょう。詳細は本ドキュメント末尾の関連リンクレシピをご覧ください。\nカメラのズーム機能 カメラのズーム機能は、ジンバルシステムのスケールを変化させることで動作します。\n# zoom settings var max_zoom = 3.0 var min_zoom = 0.5 var zoom_speed = 0.09 var zoom = 1.5 func _unhandled_input(event): if event.is_action_pressed(\"cam_zoom_in\"): zoom -= zoom_speed if event.is_action_pressed(\"cam_zoom_out\"): zoom += zoom_speed zoom = clamp(zoom, min_zoom, max_zoom) func _process(delta): scale = lerp(scale, Vector3.ONE * zoom, zoom_speed) lerp() を使用してズームレベルを変更すると、より滑らかなズーミングが可能になります。\nターゲットの追跡 カメラジンバルのセットアップが完了したら、以下の手順でターゲットを追跡できるようになります。\n@export var target : Node3D func _process(delta): if target: global_position = target.global_position シーン内のカメラを選択し、「インスペクター」を使用して追跡したいノードを選択してください。\n最終スクリプト 参考までに、カメラ設定の全変数を含む全てのスクリプトを掲載します。@export変数により、必要に応じてプロジェクト環境で設定することも可能です。\nextends Node3D @export var target : Node3D @export_range(0.0, 2.0) var rotation_speed = PI/2 # mouse properties @export var mouse_control = false @export_range(0.001, 0.1) var mouse_sensitivity = 0.005 @export var invert_y = false @export var invert_x = false # zoom settings @export var max_zoom = 3.0 @export var min_zoom = 0.4 @export_range(0.05, 1.0) var zoom_speed = 0.09 var zoom = 1.5 @onready var inner = $InnerGimbal func _unhandled_input(event): if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED: return if event.is_action_pressed(\"cam_zoom_in\"): zoom -= zoom_speed if event.is_action_pressed(\"cam_zoom_out\"): zoom += zoom_speed zoom = clamp(zoom, min_zoom, max_zoom) if mouse_control and event is InputEventMouseMotion: if event.relative.x != 0: var dir = 1 if invert_x else -1 rotate_object_local(Vector3.UP, dir * event.relative.x * mouse_sensitivity) if event.relative.y != 0: var dir = 1 if invert_y else -1 var y_rotation = clamp(event.relative.y, -30, 30) inner.rotate_object_local(Vector3.RIGHT, dir * y_rotation * mouse_sensitivity) func get_input_keyboard(delta): # Rotate outer gimbal around y axis var y_rotation = Input.get_axis(\"cam_left\", \"cam_right\") rotate_object_local(Vector3.UP, y_rotation * rotation_speed * delta) # Rotate inner gimbal around local x axis var x_rotation = Input.get_axis(\"cam_up\", \"cam_down\") x_rotation = -x_rotation if invert_y else x_rotation inner.rotate_object_local(Vector3.RIGHT, x_rotation * rotation_speed * delta) func _process(delta): if !mouse_control: get_input_keyboard(delta) inner.rotation.x = clamp(inner.rotation.x, -1.4, -0.01) scale = lerp(scale, Vector3.ONE * zoom, zoom_speed) if target: global_position = target.global_position 関連レシピ マウス操作のキャプチャ方法 3D入門ガイド –\u003e –\u003e\n","description":"","tags":null,"title":"カメラジンバル","uri":"/godot_recipes/4.x/ja/3d/camera_gimbal/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 グリッド状に移動する2Dキャラクターが必要です。\n解決策 グリッド制またはタイルベースの移動方式では、キャラクターの位置が制限されます。特定のタイル上にのみ立つことができ、2枚のタイルの間に立ち続けることはできません。\nキャラクター設定 以下がプレイヤーで使用するノードです。\nArea2D (“プレイヤー”): Area2D を使用することで、オブジェクトのピックアップや敵との衝突判定が可能になります。 Sprite2D: ここではスプライトシートを使用できます（アニメーション設定は後述します）。 CollisionShape2D: ヒットボックスが大きすぎないように注意してください。プレイヤーがタイルの中心に立つため、オーバーラップ判定も中央から行われます。 RayCast2D: 指定された方向への移動が可能かどうかを確認する際に使われます。 AnimationPlayer: キャラクターの歩行アニメーションを再生に使います。 インプットマップに操作を追加してください。この例では「上」「下」「左」「右」を使用します。\n基本的な動き まず、アニメーションや補間なしで、タイル単位の移動設定から始めてください。\nextends Area2D var tile_size = 64 var inputs = {\"right\": Vector2.RIGHT, \"left\": Vector2.LEFT, \"up\": Vector2.UP, \"down\": Vector2.DOWN} tile_sizeにはタイルのサイズに合わせて適切な値を設定してください。大規模なプロジェクトでは、メインシーンからプレイヤーインスタンスを生成する際にこを設定します。以下の例では 64×64ピクセルのタイルを使用しています。\ninputs Dictonaryは入力アクション名と方向ベクトルを対応付けます。ここでの名前とインプットマップでの表記が完全一致していることを確認してくください（大文字小文字の区別に注意！）。\nfunc _ready(): position = position.snapped(Vector2.ONE * tile_size) position += Vector2.ONE * tile_size/2 snapped() 関数は位置をタイルの増分に最も近い値に「丸め」ます。さらに、半タイル量を追加することで、プレイヤーが必ずタイル中心に配置されるようになります。\nfunc _unhandled_input(event): for dir in inputs.keys(): if event.is_action_pressed(dir): move(dir) func move(dir): position += inputs[dir] * tile_size 以下が実際の移動処理コードです。入力イベントが発生すると、4方向をチェックし、該当する方向を特定した後にmove()関数に渡して位置を変更します。\n衝突事故 以下の例のように、障害物を追加する方法は複数あります。手動で障害物を追加したい場合はStaticBody2Dオブジェクトを使用するか（グリッドに正確に配置できるようスナップ機能を有効にしてください）、衝突判定が定義されたTileMapを利用することもできます。\n移動先タイルへの進入が許可されているかどうかを判定するために、RayCast2D 機能を使用します。\n@onready var ray = $RayCast2D func move(dir): ray.target_position = inputs[dir] * tile_size ray.force_raycast_update() if !ray.is_colliding(): position += inputs[dir] * tile_size レイキャストの target_position プロパティを変更する場合、物理エンジンは次の物理フレームまで衝突を再計算しません。force_raycast_update() を使用すると、即座にレイの状態を更新できます。もし衝突が発生していなければ、移動できます。\nメモ もう一つの一般的な方法として、各方向に一つずつ計4つのレイキャストを使用する手法があります。\n動きのアニメーション化 最後にタイル間の位置補間を行い、滑らかな移動感を実現します。アニメーションにはTweenノードを使用してpositionプロパティを制御します。\nvar animation_speed = 3 var moving = false ノード Tween に参照を追加し、移動速度を設定する変数を作成します。\nfunc _unhandled_input(event): if moving: return for dir in inputs.keys(): if event.is_action_pressed(dir): move(dir) Tween が実行されている間は入力を無視し、直接的な 位置 変更を無効とすることで、Tween自体がその処理を行えるようにします。\nfunc move(dir): ray.target_position = inputs[dir] * tile_size ray.force_raycast_update() if !ray.is_colliding(): #position += inputs[dir] * tile_size var tween = create_tween() tween.tween_property(self, \"position\", position + inputs[dir] * tile_size, 1.0/animation_speed).set_trans(Tween.TRANS_SINE) moving = true await tween.finished moving = false 異なるトランジション効果を試してみます。\nプロジェクトのダウンロード プロジェクトコードはこちらよりダウンロードできます。https://github.com/godotrecipes/2d_grid_movement/\n","description":"","tags":null,"title":"グリッドベースの移動","uri":"/godot_recipes/4.x/ja/2d/grid_movement/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 ユーザーインターフェースに問題があります。複雑になりすぎている、サイズ変更がうまく機能しない、コンポーネントの配置が把握しづらい、といった点が考えられます。\n解決策 多くの開発者にとって、UI開発は最も苦手な作業です。複雑なUIは制御不能に陥りやすく、修正や変更が極めて困難な状況に陥ることがあります。Godotには優れたUI構築ツールが揃っていますが、これらを適切に使いこなすための時間を投資すれば、そうした苦労を大幅に軽減できることに気付くでしょう。\n解決策は Container コンポーネントです。コンテナを活用することで、UIのレイアウト構築が大幅に効率化できます。\nControl をContainerに追加すると、コンテナが当該コントロールの位置指定情報をすべて継承します。これにより、子要素のサイズや位置、その他のレイアウトプロパティを個別に設定することはできなくなります。\nコンテナに関する最も重要なポイントは以下の通りです。\n警告 コンテナ ノードは 子要素を自動的に配置します。直接的に特定の子UIノードの位置を制御することはできません。\n以下によく使われる代表的なコンテナをご紹介します。\nCenterContainer\nこのコンテナは子要素を中央に配置します。\nMarginContainer\nこのコンテナはマージンを保持しており、子要素がコンテナの端に近づきすぎるのを防ぎます。マージン値はプロパティの「カスタム定数」セクションで設定できます。\nVBoxContainer/ HBoxContainer\nこれらのコンテナは、内容物を垂直または水平方向に整列させます。「カスタム定数」セクションでは、要素間の間隔を広げるための分離プロパティも設定できます。\nGridContainer\nこのコンテナは子要素をグリッド状に配置します。\nサイズ指定フラグ コンテナが子要素をどのように扱うかは、主に「サイズフラグ」プロパティによって制御されます。\nFill\nこのオプションをチェックすると、コントロールはコンテナ内で指定された位置に適切に配置されます。デフォルトではこの機能が有効になっています。\nExpand\nこのオプションが有効になっている場合、コントロールはできるだけ多くのスペースを使用しようとします。「展開」機能が選択されていないノードは、それが有効になっているノードによって押し出されます。\nShrink Center\nFill が無効で Expand が有効になっている場合、コントロールはエリアの先頭ではなく、中央に配置されたままになります。\nShrink End\n上記と同様ですが、制御は開始位置ではなく終了位置に設定されます。\nStretch Ratio\nこの比率設定により、拡張コントロールの相対的な占有量が決定されます。\nこれらの設定を試すには、以下のようなテストシーンを設定する方法が有効です。\n異なるボタンの「サイズフラグ」プロパティを調整してみて、それが HBoxContainer 内での配置にどのように影響するか確認してください。\nネストされたコンテナ より複雑なUI構成の場合は、他のコンテナを保持するコンテナを使用が必要です。例えばGridContainer内の各アイテム自体がVBoxContainerであり、その全てがMarginContainer内に配置されている、といった構造になります。\nこれらのコンテナをネストした構造は、シーンツリーが非常に複雑になり、管理が困難になる原因となります。特にボタンやラベルなど繰り返し使用する要素が多い場合に顕著です。UIは適切な単位で分割し、各部分を個別のシーンとして保存することをオススメします。これにより、必要に応じて大規模なシーン内でインスタンス化できるようになります。\n関連レシピ ラベル ","description":"","tags":null,"title":"コンテナ","uri":"/godot_recipes/4.x/ja/ui/containers/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 GDScriptからGodotシェーダーと連携したい。\n解決策 GDScriptから uniform の値にアクセスするには、オブジェクトのmaterialプロパティに対してset_shader_param()メソッドを使用できます。もしアタッチされているマテリアルが ShaderMaterial の場合、以下のようにアクセス可能です。\nnode.material.set_shader_param(\"param_name\", value) 以下の方法で値を取得することもできます。get_shader_param()。\n例については、ブラーシェーダーのレシピを参照してください。\n関連するレシピ シェーダー入門 ","description":"","tags":null,"title":"シェーダーとの連携","uri":"/godot_recipes/4.x/ja/shaders/interacting/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 以下のような事前に定義された経路を移動するキャラクターが必要です。警備員が巡回する場面や、車両が道路を走るシーンなど。\n解決策 この問題に対処する方法は複数あります。ここでは一例として、エディター内でパスを描画するための便利な方法として、GodotのPath2Dノード（3Dの場合は Path）を使用します。\n以下の方法ができます。メインシーン、マップ、またはその他適切な場所にPath2Dを子要素として追加できます。ただし、巡回エンティティの子要素に設定しないようご注意ください - そうすると経路がプレイヤーと一緒に移動してしまいます！\n経路の描画 Path2Dノードを追加すると、ビューポート上部に新しいボタンが表示されます。\n「ポイント追加」ボタンを選択してクリックし、追加を開始します。閉じた曲線を作成したい場合は、「曲線を閉じる」ボタンをクリックすると最後の点が最初の点に接続されます。\n「制御点」モードを使用して、線の「曲線度」を調整できます。\n経路に沿った移動 以下のタグを使用して経路に沿って自動移動させることができます。PathFollow2D。ただし、キャラクターボディを使用している場合、この方法は衝突判定に問題を引き起こす可能性があります。これはボディの移動メソッドを利用していないためです。このため、代わりに経路上の各点を「目標」として設定し、ボディがその方向へ移動するようにします。\nextends CharacterBody2D var move_speed = 100 @export var patrol_path: NodePath var patrol_points var patrol_index = 0 var velocity = Vector2.ZERO func _ready(): if patrol_path: patrol_points = get_node(patrol_path).curve.get_baked_points() patrol_pathをエクスポートすることで、インスペクター内で直接パスノードを割り当てることができます。その後、そのノードが割り当てられていれば、_ready()関数内でラインを構成するポイント情報を取得可能です。\n次のステップとして、現在パス上で選択されている点を移動先として使用できます。十分に近づけると、曲線の次のポイントに移動し、wrapi() 関数を使用して終点に到達したら最初のポイントに戻るループ処理を行います。\nfunc _physics_process(): if !patrol_path: return var target = patrol_points[patrol_index] if position.distance_to(target) \u003c 1: patrol_index = wrapi(patrol_index + 1, 0, patrol_points.size()) target = patrol_points[patrol_index] velocity = (target - position).normalized() * move_speed velocity = move_and_slide(velocity) 関連レシピ ","description":"","tags":null,"title":"パスを追従する","uri":"/godot_recipes/4.x/ja/ai/path_follow/index.html"},{"content":"最後のセクションでは、プロジェクトを設定し、ゲームアートをダウンロードしました。これでコーディングを開始する準備が整いました。まずはプレイヤー操作可能な宇宙船から着手します。\n船シーンの設定 Godot ワークフローにおいて共通的な作業の一つにシーンの作成があります。前述したように、Godotにおける「シーン」とは、単なる複数の「ノード」の集合体に過ぎません。ほとんどのGodotプロジェクトでは、各ゲームオブジェクトは個別のシーンとして構成され、目的に応じた機能を提供するノードと、必要に応じて動作をカスタマイズするスクリプトが組み込まれています。\nノードの選択方法 最初のステップは、どのタイプのノードから始めるかを決めることです。シーンに追加する最初のノードは「ルートノード」と呼ばれます。通常、シーンのルートノードはゲームオブジェクトの主要な動作を定義するものでなければなりません。その後、追加機能を実現するために「子ノード」を取り付けていきます。\nさて、ゲームに登場する船はどんなデザインにすべきでしょうか？要件を整理し、それらを満たすために活用できそうなノードを検討してみます。\nその船には以下が必要です。\n2D空間での移動 : これには基本的なNode2Dノードで十分です。なぜならこのノードにはposition、rotationなどの2D関連プロパティが備わっているからです。ただし、外観に関する要素はありません。\n画像を表示 : Sprite2Dはこの目的のためのノードです。これもNode2Dであるため、自由に移動させることができます。\n衝突検出機能：画面内で敵キャラが射撃したり移動したりするため、自機がダメージを受けたタイミングを正確に把握する必要があります。固い物体同士の相互作用（跳ね返りや運動量伝達など）を考慮する必要はなく、単に接触を検知できれば十分です。この用途にはArea2Dコンポーネントが最適です。他オブジェクトとの接触検出機能を備え、位置関連プロパティを持っていますが、独自の視覚表現は持ちません。\nこのリストを見ると、Area2Dが主要な機能を提供していることがわかります。ここにSprite2Dをアタッチして宇宙船の画像を表示すれば、必要なものはすべて揃います。\nシーンの構築方法 シーン タブで、＋ ボタンまたはその他のノードを追加 ボタンをクリックして最初のノードを作成します。Area2D と入力し、リストから選択してください。ノードが シーン タブに追加されたら、その名前をクリックして Player に改名し、\u003cCtrl+S\u003e キーを押してシーンを保存します。\n船舶情報の表示 Playerノードを選択した状態で、もう1つのノードを追加してください。Sprite2D。整理しやすくするため、このノードの名前をShipに変更してください。\nファイルシステム タブから、アートパック内の Player_ship (16x16).png ファイルをドラッグし、 インスペクター の テクスチャ プロパティにドロップしてください。\n最初に気づくのは、どうやら船が3隻あるように見える点です！アートパックに含まれている画像には、左右へ移動するバージョンも含まれています。これを利用してみます - ［インスペクター］の［アニメーション］セクションで、Hフレーム数を3に設定します。これでフレームプロパティを変更すると、これら3つの異なるバージョン間を切り替えられるようになります。今のところは1のままにしておいてください。\n衝突形状の追加方法 また、Area2Dノードの横にある黄色の警告三角アイコンにもお気づきかもしれません。このアイコンをクリックすると、このエリアに形状が定義されていないという警告メッセージが表示されます。適切に対応するためには、Playerノードの下にCollisionShape2Dノードを追加が必要です。これにより、オブジェクト同士が衝突判定するための正確な境界を設定できます。\nこのノードのインスペクタウィンドウで形状プロパティを確認すると、現在は \u003cempty\u003e と表示されています。このボックスをクリックすると、様々な形状を選択できるドロップダウンメニューが表示されます。ここでは . New RectangleShape2D を選択して追加すると、船の上に水色の正方形が表示されるはずです。図形のサイズは、オレンジ色の円をドラッグして調整できます。または、形状 プロパティ内の Shape をクリックして拡大表示し、手動で サイズ を入力することも可能です。\n排気システム アニメーションを追加すると、船の見た目がよりダイナミックになります。アートパックには「ブースター」と名付けた排気炎のアニメーションが3種類含まれています。それぞれ左舷用、前進時用、右舷用に対応しています。\nこれらを表示するには、Shipノードを選択し、子要素としてAnimatedSprite2Dノードを追加し、「ブースター」という名前を付けてください。\nインスペクターのアニメーション セクション内にあるSprite Framesプロパティは、現在 \u003c空\u003e と表示されています。これをクリックすると新しい SpriteFramesが作成され、エディタウィンドウ下部にあるアニメーションパネルを開くために SpriteFrames 項目をクリックできるようになります。\n「デフォルト」アニメーションをダブルクリックして名前を「前進」に変更します。次に、アニメーション画像を追加するには、スプライトシートからフレームを追加 ボタンをクリックします。\nBoosters (16 x 16).png画像を選択すると、「フレーム選択」ウィンドウが表示され、必要なフレームを選択できるようになります。\nこのアニメーションにはフレームが2つしかありませんが、グリッドが正しくありません。画像サイズに合わせて サイズ の値を変更してください。16 x 16。その後、両方のフレームをクリックして選択し、フレームを追加(2個) ボタンをクリックしてください。\n2つのフレームを追加したら、再生ボタンを押してアニメーションを実行してください。また、ロード時に自動再生ボタンを切り替えれば、自動的にアニメーションが開始されるようになります。\n処理が少し遅いので、フレームレートを10 FPSに変更してください。\n追加アニメーションを2つ作成するには、[アニメーションを追加] ボタンをクリックして、それぞれ left(左) と right(右) と命名してください。\nプロセスを繰り返し、左右の「ブースター」スプライトシートを追加してください。\n銃のクールタイム時間 最後に設定する必要があるのは、プレイヤーの射撃速度を制御するTimerです。Playerノードの下にTimerを追加し、名前をGunCooldownとします。One Shot プロパティを「オン」に設定します。これにより、タイマー終了後に自動リセットされなくなります。実際の実装では、プレイヤーが銃を撃つタイミングでタイマーを起動させ、タイマーが切れるまでは再射撃できないようにします。\n次のステップ プレイヤーシーンの設定はこれで完了です。ゲーム内でプレイヤーが操作する艦船に必要な機能を実装するため、ノードを追加しました。次のセクションでは、プレイヤーが艦船を制御し、射撃を行い、物体と衝突したことを検知するためのコードを追加してゆきます。\n戻る 次へ ","description":"","tags":null,"title":"プレイヤーシーンの設計","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_02/index.html"},{"content":"課題 マウス入力を検出したい。\n解決策 InputEventMouse はマウスイベントの基本クラスです。これには position および global_position プロパティが含まれています。このクラスを継承するサブクラスとして、InputEventMouseButton と InputEventMouseMotion の2つがあります。\nメモ インプットマップでマウスボタンイベントを割り当てられるので、is_action_pressed() 関数を使用してこれらを利用できます。\nInputEventMouseButton @GlobalScope.ButtonList には、各可能なボタンに対応する定数リスト（例：BUTTON_*）が格納されており、これらの値はイベントのbutton_indexプロパティで報告されます。なお、スクロールホイールも1つのボタンとしてカウントされます - 正確には2つのボタンがあり、それぞれBUTTON_WHEEL_UPとBUTTON_WHEEL_DOWNという別々のイベントとして扱われます。\nヒント 通常のボタンとは異なり、マウスホイールクリックではpressed(押された)イベントのみが発生します。マウスホイールクリックが「離された」というイベントはありません。\nfunc _unhandled_input(event): if event is InputEventMouseButton: if event.button_index == BUTTON_LEFT: if event.pressed: print(\"Left button was clicked at \", event.position) else: print(\"Left button was released\") if event.button_index == BUTTON_WHEEL_DOWN: print(\"Wheel down\") InputEventMouseMotion これらのイベントはマウスが移動するたびに発生します。移動距離（画面座標単位）は「relative」プロパティで取得できます。\n以下に、マウス操作による3Dキャラクターの回転動作を実装した具体例を紹介します。\n# Converts mouse movement (pixels) to rotation (radians). var mouse_sensitivity = 0.002 func _unhandled_input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) ","description":"","tags":null,"title":"マウス入力","uri":"/godot_recipes/4.x/ja/input/mouse_input/index.html"},{"content":" 基本編 あらゆるプロジェクトに応用できるGodotの基本テクニックと便利なコツ\n目次 ： ツリー順序を理解しよう ノードパスを理解しよう ノード間のやりとり（良い方法） 理解しよう！'delta' データを保存・読み込む Godot 3.x からの移行 円運動 カスタムリソースを使用 ","description":"","tags":null,"title":"基本編","uri":"/godot_recipes/4.x/ja/basics/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 プレイヤー／モブなどから投射物を発射させたい。\n解決策 弾丸の設定 まず、インスタンス化可能な「弾丸」オブジェクトを設定します。使用するノードは以下の通りです。\nArea2D: Bullet Sprite2D CollisionShape2D Sprite2Dのテクスチャは、好きな画像を使用できます。以下は例です。\nノードの設定とスプライトおよび衝突判定形状を構成します。テクスチャが上向きに配置されている場合（上記例のように）は、Sprite ノードを 90° 回転させて右方向に向け、親オブジェクトの「前方」方向に一致するように調整してください。\nスクリプトを追加し、Area2Dのbody_enteredシグナルに接続してください。\nextends Area2D var speed = 750 func _physics_process(delta): position += transform.x * speed * delta func _on_Bullet_body_entered(body): if body.is_in_group(\"mobs\"): body.queue_free() queue_free() この例では、オブジェクトが何かを衝突した場合、即座に弾丸を除去します。また、「mobs」グループにタグ付けされた対象物もすべて削除します。\n射撃 弾丸の出現位置を設定が必要です。Marker2Dコンポーネントを追加し、弾丸を出現させたい場所に配置してください。以下は具体例で、銃身の先端に設置しています。「Muzzle」という名前を付けています。\n「プレイヤーが回転するにつれ、Muzzleのtransformは銃に対して常に同じ向きを保つことに注目してください。これは弾丸をスポーンさせる際に非常に便利です。変換行列を使用することで、適切な位置と方向を簡単に取得できるからです。新しい弾丸のtransformは、単にMuzzleのものと等しく設定するだけで済みます。\nヒント この手法は「回転・移動」スタイルに限らず、あらゆる文字タイプに適用できます。単に、弾丸を表示させたい位置に Marker2D タグを挿入するだけで済みます。\nキャラクタースクリプト内で、インスタンス化用の弾丸シーンを保持する変数を追加してください。\n@export var Bullet : PackedScene 入力アクションが定義されているか確認してください。\nif Input.is_action_just_pressed(\"shoot\"): shoot() これでshoot()関数内では、弾丸インスタンスを生成しツリーに追加できます。よくあるミスとして、プレイヤーノードの子要素として直接追加してしまうケースがあります\nfunc shoot(): var b = Bullet.instantiate() add_child(b) b.transform = $Muzzle.transform 問題は、弾丸がプレイヤーの子オブジェクトであるため、プレイヤーが移動または回転した際に影響を受ける点です。\nこの問題を解決するには、弾丸をワールドに追加が必要です。ここではプレイヤーのシーンルートノードを参照する owner 変数を使います。ただし、銃口の グローバル 変換行列も適用する必要がある点に注意してください。これを行わないと、弾丸が想定した位置に表示されない可能性があります。\nfunc shoot(): var b = Bullet.instantiate() owner.add_child(b) b.transform = $Muzzle.global_transform 関連レシピ ゲーム数学：トランスフォーム操作 –\u003e\nプロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。https://github.com/godotrecipes/2d_shooting\n","description":"","tags":null,"title":"投射物の発射","uri":"/godot_recipes/4.x/ja/2d/2d_shooting/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 Godotの「入力アクション」システムについて理解を深めたい。\n解決策 例えば、見下ろし型キャラクターのゲームを開発していて、InputActionKeyを使って矢印キーで移動操作するコードを書いたとします。すると、多くのプレイヤーが「WASD」スタイルの操作方法を好き好んで使っていることに気づくと思います。後からコードに戻って追加のキーチェックを実装することもできますが、これでは重複した冗長なコードが生じてしまいます。\n入力アクションを活用することで、コードの設定可能性を高めることができます。特定のキーをハードコーディングする代わりに、コードを変更せずに動的に調整・カスタマイズできるようになります。\n入力の作成方法 入力操作は「プロジェクト設定」の「インプットマップ」タブで定義します。ここでは新しいアクションを作成するか、既存のアクションに入力を割り当てることができます。\nタブをクリックすると、すでにデフォルトアクションが設定されていることがわかります。これらはすべて「ui_*」という名前になっており、これがデフォルトのインターフェース操作であることを示しています。例えば、次のUI要素に移動する「Tab」キーなどがあります。\n一般的に、既存のアクションを使用するよりも、自分のゲーム用に独自のアクションを作成する方がよいでしょう。\nこの例では、プレイヤーがキーボードまたはマウスでゲームを操作できるようにしたいとします。プレイヤーは左クリックボタンを押すか、スペースバーを押すことで、射撃ができるようにしなければなりません。\n以下の手順で新しいアクション「シュート」を作成してください。\n最上部の「アクション」欄に名称を入力します 「追加」（またはEnterキー）をクリックします 画面を一番下までスクロールすると、新しく作成したアクションがリストに追加されているのが確認できるはずです 以下の手順でこのアクションに入力を割り当てることができます。右側にある「+」記号をクリックしてください。使用可能な入力には、キーボードキー、マウスボタン、またはジョイスティック/ゲームパッド入力があります。「キー」を選択したら、割り当てたいキーボード上のキーを押します - ここではスペースバーを押してみます - そして「OK」をクリックして確定します。\n「+」をクリックして別の入力を追加してください。今回は「マウスボタン」を選択します。デフォルト設定の「デバイス 0」と「左ボタン」で問題ありませんが、お好みで他のオプションも選択できます。\n入力アクションの使用について 以下の方法でアクションをチェックできます。\n各フレームごとに単一インスタンス Input をポーリングする方法： func _process(delta): if Input.is_action_pressed(\"shoot\"): # This will execute every frame as long as the input is held. これは、継続的な動作――例えば移動など、常時確認が必要な状況に最適です。\nもし代わりに、アクションが発生したその瞬間に検出したい場合は、_input()または_unhandled_input()コールバックを使用できます。\nfunc _unhandled_input(event): if event.is_action_pressed(\"shoot\"): # This will run once on the frame when the action is first pressed 入力状態を確認するために使える関数があります。\nis_action_pressed(): この関数は現在アクションが押された状態にある場合にtrueを返します。\nis_action_released(): この関数は、アクションが pressed 状態にない場合に true を返します。\nis_action_just_pressed() / is_action_just_released(): これらのメソッドは上記と同様の機能を持ちますが、イベント発生後の1フレーム目にのみtrueを返す点が異なります。射撃やジャンプなど、ユーザーがキーを放した後に再度押して動作を繰り返す必要がある非反復アクションに特に有用です。\n関連するレシピ 入力システム入門 ","description":"","tags":null,"title":"入力アクション","uri":"/godot_recipes/4.x/ja/input/input_actions/index.html"},{"content":"課題 対象物を滑らかに追従できる3Dカメラが必要となります（補間機能が必要となります）。\n解決策 情報 Godotに標準搭載されているInterpolatedCameraノードは非推奨となっており、Godot 4.0リリース時に廃止される予定です。\n以下のスクリプトをシーン内のCamera3Dノードにアタッチしてください。3つのexportプロパティにより、以下のように選択できます。\nlerp_speed - カメラの移動速度。値を小さくすると「動きが鈍い」印象になります target - カメラのターゲットノードを選択します offset - ターゲットに対するカメラの相対位置設定です 以下に、実際にカメラを使用した例をご紹介します。\nextends Camera3D @export var lerp_speed = 3.0 @export var target: Node3D @export var offset = Vector3.ZERO func _physics_process(delta): if !target: return var target_xform = target.global_transform.translated_local(offset) global_transform = global_transform.interpolate_with(target_xform, lerp_speed * delta) look_at(target.global_transform.origin, target.transform.basis.y) _physics_process()関数内では、カメラの位置をtargetの位置（と、offsetを加算した値）に補間します。\n使用例 lerp_speed: 3.0 offset: (0, 7, 5) ","description":"","tags":null,"title":"補間カメラ","uri":"/godot_recipes/4.x/ja/3d/interpolated_camera/index.html"},{"content":"このチュートリアルを読み進める前に、まずベクトルの概念とゲーム開発における使用方法を理解する必要があります。もし不十分と感じている場合、Godotドキュメントの、以下の入門記事を読むことを強くオススメします。 ベクトル演算\n2Dトランスフォーム 2D空間では、おなじみのX軸・Y軸座標平面を使用します。Godotでは（ほとんどのコンピュータグラフィックスアプリケーションと同様に）、Y軸は下向きに定義されていることに注意してください。\nまず、宇宙空間を漂流しているこの宇宙船について考えてみます。\n船の進行方向は座標軸のX軸と同じ方向を向いています。これを前進させたい場合は、X座標に値を加えることで右方向へ移動させられます。\nposition += Vector2(10, 0) しかし、船が回転した場合はどうなるのでしょうか？\n船を前方に移動させるにはどうすれば良いでしょうか？学校で三角関数を学んでいた方なら、角度やサイン・コサインの概念を思い出し、position += Vector2(10 * cos(angle), 10 * sin(angle))のような計算式を考えつくかもしれません。この方法でも実現できますが、より便利な方法があります。それは トランスフォーム(Transform) です。\nもう一度回転した船を見てみてください。今回は、その船が独自の X 軸と Y 軸を持っており、それらは世界的な座標系とは独立して移動していることを想定します。\nこれらの「ローカル」座標軸は、オブジェクトの transform プロパティに含まれています。\nこの特性を利用すれば、船をX軸に沿って移動させることで簡単に前進できます。角度計算や三角関数を気にする必要もありません。Godotでこれを実現するには、すべてのNode2D の派生ノードで利用できるtransformプロパティを使用します。\nposition += transform.x * 10 このコードは「変換の x ベクトルに 10 を乗算した結果を追加する」という意味です。これを分解して説明します。transform オブジェクトには x と y プロパティがあり、これらはローカル座標軸を表しています。これらは「単位ベクトル」であり、その長さは 1 です。単位ベクトルは別の言い方で「方向ベクトル」とも呼ばれます。これは船の x 軸が指し示す方向を示しています。この値に 10 を掛けることで、より長い距離に対応するようにスケール変換しています。\nヒント ノードの transform プロパティは親ノードに対する相対値です。グローバル座標が必要な場合は、global_transform を参照してください。\nローカル座標系に加えて、この変換には origin(原点) と呼ばれるコンポーネントも含まれています。原点は位置の 移動量、すなわち変化した位置を表します。\nこの画像では、青色のベクトルが transform.origin です。これはオブジェクトの position ベクトルと等しくなります。\nローカル空間とグローバル空間での座標変換 座標を局所座標系からグローバル座標系に変換するには、変換を適用します。利便性を考慮し、Node2D と Node3D にはこの処理を簡単に行う補助関数が用意されています。to_local() と to_global()。\nvar global_position = to_global(local_position) 2次元平面上のオブジェクトを例に取り、マウスクリック座標（グローバル空間）をオブジェクト相対座標に変換する方法を説明します。\nextends Sprite func _unhandled_input(event): if event is InputEventMouseButton and event.pressed: if event.button_index == BUTTON_LEFT: printt(event.position, to_local(event.position)) 利用可能なプロパティとメソッドの一覧については、Transform2Dドキュメントを参照してください。\n3D トランスフォーム 3D空間において、変換方法の捉え方は2D空間で適用される場合と全く同様です。実際、3次元で角度を扱う場合には様々な問題が生じる可能性があるため、この重要性はさらに高まります。これについては後ほど詳しく説明します。\n3Dノードは基本ノードNode3Dを継承しており、変換情報を保持しています。3D空間におけるトランスフォームは、2D版に比べてより複雑な情報を必要とします。位置座標はこれまで通りoriginプロパティで管理されますが、回転情報は新たに追加されたbasisプロパティに格納されます。このプロパティには、オブジェクトのローカル座標系におけるX軸、Y軸、Z軸を表す単位ベクトルが3つ含まれています。\nエディターで3Dノードを選択すると表示されるギズモを使用すると、変換操作を行えます。\nローカル空間モード エディタ内で「ローカル空間を使用」ボタンをクリックすると、オブジェクトの局所的な向きを表示・操作できます。 このモードでは、「ローカル空間モード」が有効になっている間、3本の色分けされた軸線がオブジェクトの局所座標系の基底軸として表示されます。\n2Dと同様に、ローカル軸を使ってオブジェクトを前方に移動させることができます。Godotの3D座標系（ Y軸正方向 ）では、デフォルトでボディの -Z軸 が前進方向になります。前に進むには。\nposition += -transform.basis.z * speed * delta ヒント Godotにはデフォルトのベクター値が定義されています。例えば：Vector3.FORWARD == Vector3(0, 0, -1)。詳細はVector2およびVector3のドキュメントを参照してください。\n","description":"","tags":null,"title":"トランスフォーム","uri":"/godot_recipes/4.x/ja/math/transforms/index.html"},{"content":" 2D ゲーム開発における2D領域のヒント、テクニック、チュートリアル情報を提供します。\n目次 ： タイルマップ：タイルを検出する プラットフォームキャラクター 画面ループ 画面への入力／出口 見下ろし型での移動 分割画面マルチプレイヤー グリッドベースの移動 投射物の発射 車のステアリング操作 8方向移動・アニメーション Yソートを使う タイルマップ：自動タイルを使用 2Dグリッド上での経路探索 アニメーションタイル(タイルマップ) コヨーテタイム 動く床 マルチターゲットカメラ Line2D衝突判定 タッチスクリーンカメラ 弾道銃弾 軌跡を描画 ","description":"","tags":null,"title":"2D","uri":"/godot_recipes/4.x/ja/2d/index.html"},{"content":"前回の解説では、3Dオブジェクトのインポート方法とシーン内での配置方法について取り上げました。今回のチュートリアルでは、さらにオブジェクトを追加し、ユーザー操作可能なキャラクターも実装していきます。\nシーンの構築 引き続き、パート2でダウンロードしたKenney Platformer Kitを使用します。すべてのblock*.glbファイルを選択し、インポートタブでそのルートの型をStaticBody3Dに設定してください。ルート名プロパティはチェックを外しておき、再インポートをクリックします。blockLarge.glbを選択して新しい継承シーンを作成します。前回のチュートリアルと同様に、メニューからメッシュに対して 「Collision Shape Placement」で兄弟 オプションを使いましょう。これでシーンを保存できます。また別にフォルダを作成することをオススメします。なぜなら、すぐに様々な形状のプラットフォームパーツを表すシーンが大量に出来上がるからです。\n前のステップで作成した「Ground」平面と木箱を含むシーンを開いてください。 まず、木箱を削除し、大型ブロックのインスタンスを追加してください。これらのブロックを整列させて配置できるようにしたいので、以下の手順を実行してください。 ビューポート上部の「Transform」メニューから「スナップ設定」を選択します → 移動スナップ を 0.5 に設定します。 次に、「スナップモード」ボタンをクリックするか（または Y キーを押す）ことでスナップモードを有効にします。これでブロックを複製し、ドラッグして配置できるようになります。\nご希望であれば、他のプラットフォームブロック用のシーンを追加し、 魅力的なレベルに構成してみます。創造力を発揮してください！\nキャラクターの追加方法 次に、プラットフォーム上を歩き回れるようにキャラクターを作成します。 新しいシーンを開き、“Character\"という名前のCharacterBody3Dコンポーネントを追加してください。このPhysicsBodyノードは2D版とほぼ同じ動作をします（2Dチュートリアルはお済みですか？）。このコンポーネントにはmove_and_slide()メソッドが用意されており、これを使って移動と衝突検出を行います。\nメッシュインスタンス3D用のカプセル型MeshInstance3Dと、それに対応させた衝突形状CollionShape3Dを追加してください。また、メッシュにStandardMaterial3Dを追加してアルベド/カラープロパティを設定すれば、色を変更できることも覚えておいてください。\nカプセル自体は良好ですが、回転方向を正確に把握するのは困難です。そこで、もう一つのメッシュを追加してください。今回は CylinderMesh3D 形状を使用します。 Top Radius を 0.2、Bottom Radius を 0.001、Height を 0.5 に設定し、その後 x軸 回転を -90度に変更します。これで円錐形の形状が完成します。ボディから外側に向かって伸びるように、このメッシュを配置してください。その際、負方向の z軸 に沿って配置します（ギズモの矢印が正方向に表示されている方が、マイナス方向です）。\nこの画像では、さらにキャラクター性を高めるために、目用に2つの球状メッシュを追加しています。ご自由にお好みのディテールを追加してください。\nシーンに Camera3D オブジェクトも追加してください。プレイヤーが移動すると追従するように設定します。キャラクターの後方かつ少し上方に配置し、わずかに下向きに角度を調整してください。「プレビュー」ボタンをクリックするとカメラ視点で確認できます。\nスクリプトを追加する前に、「プロジェクト設定」を開き、「インプットマップ」タブで以下の入力を追加してください。\nアクション キー move_forward W move_back S strafe_right D strafe_left A jump Space それでは、ボディ部分にスクリプトを追加してください。\nextends CharacterBody3D var gravity = ProjectSettings.get_setting(\"physics/3d/default_gravity\") var speed = 4.0 # movement speed var jump_speed = 6.0 # determines jump height var mouse_sensitivity = 0.002 # turning speed func get_input(): var input = Input.get_vector(\"strafe_left\", \"strafe_right\", \"move_forward\", \"move_back\") velocity.x = input.x * speed velocity.z = input.y * speed func _physics_process(delta): velocity.y += -gravity * delta get_input() move_and_slide() _physics_process()内のコードは非常にシンプルです。重力を加えて Y方向（下向き） に加速し、get_input()を呼び出して入力をチェックした後、move_and_slide()を使用して速度ベクトルの方向に移動させます。\nget_input() 関数では、押されたキーを確認し、対応する方向に移動します。プログラムを実行してテストしてみます。\nこれは素晴らしい設定ですが、マウスで回転できるようにしたいですよね。キャラクターのスクリプトに以下のコードを追加してください。\nfunc _unhandled_input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) これにより、x方向のマウス移動はすべて、y軸を中心とした回転に変換されます。\nシーンを実行し、マウスを動かすとキャラクターが回転することを確認してください。\nしかし、問題があります。どちらの方向を向いていようと、W を押すとワールド座標の Z軸 に沿って移動してしまいます。したがって、現在の移動オブジェクトを進行方向に移動が必要です。\nトランスフォームの力 ここで トランスフォーム が登場します。トランスフォームとは、オブジェクトの位置・回転・スケール情報をすべて含む数学的な 行列 です。Godotではこの情報は Transform データ型に格納されます。位置情報は transform.origin と呼ばれ、向きの情報は transform.basis に含まれます。\n3Dギズモを『ローカル空間モード』に設定できることを思い出してください。このモードで動作させると、 ギズモのX/Y/Z 軸はオブジェクトそのものの座標系に沿って配置されます。これは変換の basis と同じ概念です。基底には x, y, z という3つの Vector3 オブジェクトが含まれており、これらが各方向を表します。この機能を利用することで、Wキーを押すと常にオブジェクトの進行方向に移動させることができます。\nget_input()関数を次のように変更します\nfunc get_input(): var input = Input.get_vector(\"strafe_left\", \"strafe_right\", \"move_forward\", \"move_back\") var movement_dir = transform.basis * Vector3(input.x, 0, input.y) velocity.x = movement_dir.x * speed velocity.z = movement_dir.z * speed 入力ベクトルに transform.basis を掛けることで、その変換をベクトルに『適用』します。基底はオブジェクトの回転を表すため、これにより前方方向と後方方向がオブジェクトのZ軸方向に、サイド移動キーがそのX軸方向に沿った向きに変換されます。\nジャンプ動作 プレイヤーにさらに1つの動作を追加してください。ジャンプです。\n以下の行を _unhandled_input() の最後に追加してください。\nif event.is_action_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed カメラ機能の改善 お気づきかもしれませんが、キャラクターが障害物近くに立っている場合、カメラが「オブジェクト内部に切り込む」（クリッピング現象）ことがあり、見た目が美しくありません。本格的な3Dカメラの実装は複雑なテーマですが、Godotに組み込まれたノードを活用することで、実用的な解決策を得ることができます。\nキャラクターシーンから Camera3D を削除し、代わりに SpringArm3D を追加してください。このノードは移動可能なアームとして機能し、カメラを保持しながら衝突検知を行います。障害物がある場合は、カメラを障害物に近づけるように自動調整します。\nプロパティ設定で、スプリング長さを5に、位置を(0, 1, 0)に設定します。これはキャラクターの頭部に位置する値です。スプリング長さを示す黄色のラインに注意してください。カメラはこのラインに沿って移動します - 可能であれば終点まで進み、障害物がある場合はより近付く方向に動きます。\nSpringArm3Dの子要素として再び Camera3D を追加し、再度ゲームを実行してみてください。スプリングアームを回転させて試してみることができます（例えばx軸を中心に少し下向きに調整するなど、好みの配置が見つかるまで自由に操作してください）。\nファーストパーソンはどう実装する？ ファーストパーソン視点での実装方法に興味がある場合は、基本FPSキャラクタ作成レシピを参照してください。先ほど書いたサードパーソン用スクリプトと共通点がありますね。\nまとめ このチュートリアルでは、より複雑なシーンの構築方法と、ユーザー操作キャラクター用の移動制御コードの書き方を学びました。また、3Dグラフィックスにおいて極めて重要な概念であるトランスフォームについても理解を深めました。これらは今後のプロジェクトで頻繁に活用する技術となるでしょう。\n","description":"","tags":null,"title":"3Dキャラクターを作る","uri":"/godot_recipes/4.x/ja/g101/3d/101_3d_03/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 車（またはその他の物体）を追跡できる「追従カメラ」を実装したい。\n解決策 メモ Godot には組み込みの InterpolatedCamera ノードが用意されており、ここで説明している機能の大部分をすでに実装しています。ただし、このノードを使用しない理由は2つあります。まず、キャラクターボディを追いかける際にカクツキが発生しやすい点、次に Godot 4.0 で廃止予定となっている点です。とはいえ、独自のセットアップは非常に簡単ですので、ご安心ください。\nカメラのセットアップ方法 新しいシーンをCamera3Dで追加してください。名前はChaseCameraとし、保存してからスクリプトを追加してください。\nChaseCameraには追跡対象のtargetが設定されます。また、必要に応じてこのターゲットを変更する機能も実装します。\nextends Camera @export var lerp_speed = 10.0 var target = null func _physics_process(delta): if !target: return global_transform = global_transform.interpolate_with(target.global_transform, lerp_speed * delta) func _on_change_camera(t): target = t この設定で重要なのはlerp_speedパラメーターのみです。これはカメラが位置を更新する速度を調整する値です。値を小さくするとカメラは車の動きに「遅れて」追従し、大きく設定すると常に車両に固定された状態になります。\nターゲットの設定 複数の異なるカメラポジションを設定したいと考えています。例えば、近距離用と遠距離用の2種類、あるいは真下を見下ろすアングルなどです。車オブジェクトに Node3D を追加し、名前を CameraPositions とします。さらにこのオブジェクトに、必要な数だけ Marker3D を配置してください。\nMarker3D をお好みの位置に移動・配置してください。位置の -Z 軸は車両に向けるようにしてください。\nヒント 作業効率を上げるため、一時的に Camera を適切な位置に配置し、「プレビュー」モードを使用して、Marker3D が正確に狙いたい方向を指すように調整する方法が有効です（作業完了後はカメラを削除してください）。 カメラと通信するために、位置変更が必要な時にシグナルを発します。以下のコードを車のスクリプトに追加してください。\nextends \"res://cars/car_base.gd\" signal change_camera var current_camera = 0 @onready var num_cameras = $CameraPositions.get_child_count() func _ready(): emit_signal(\"change_camera\", $CameraPositions.get_child(current_camera)) func _input(event): if event.is_action_pressed(\"change_camera\"): current_camera = wrapi(current_camera + 1, 0, num_cameras) emit_signal(\"change_camera\", $CameraPositions.get_child(current_camera)) インプットマップにカメラ切り替え用のアクションを追加してください。ここではTabキーと右ショルダーボタンを使用しています。\n接続方法 メインシーンにChaseCameraインスタンスを追加し、現在のカメラとして設定してください。その後、車のchange_cameraシグナルをカメラの_on_change_angle()関数に接続します。\nゲームを起動し、カメラ切替ボタンを押して試してみてください。\n関連レシピ 運動車：基本モデル 2D: 車両のステアリング制御レシピ 入力アクション設定 3D: CharacterBody3Dの移動機能 この動画が気に入ったら？ ","description":"","tags":null,"title":"3Dで自動車を作ろう：カメラで追いかけよう","uri":"/godot_recipes/4.x/ja/3d/kinematic_car/car_camera/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 CharacterBody3D が坂道を滑り落ちてしまいます。\n解決策 まず、最小限の機能で構成された CharacterBody3D から始め、以下のスクリプトで move_and_slide() メソッドを使用しています。\nextends CharacterBody3D @export var gravity = -10.0 @export var speed = 5.0 @export var rot_speed = 4.0 @export var jump_speed = 5.0 var velocity = Vector3.ZERO var jumping = false func get_input(delta): var input = Vector3.ZERO if Input.is_action_pressed(\"forward\"): input += -transform.basis.z * speed if Input.is_action_pressed(\"back\"): input += transform.basis.z * speed if Input.is_action_pressed(\"right\"): rotate_y(-rot_speed * delta) if Input.is_action_pressed(\"left\"): rotate_y(rot_speed * delta) velocity.x = input.x velocity.z = input.z func _physics_process(delta): get_input(delta) velocity.y += gravity * delta move_and_slide() if jumping and is_on_floor(): jumping = false if Input.is_action_just_pressed(\"jump\"): if is_on_floor(): jumping = true velocity.y = jump_speed 傾斜地で動きを止めると、問題が明らかになります。\nこれがmove_and_slide()の本来の動作です。\n重力によって生じる落下速度が、表面に沿って滑動しています。\nmove_and_slide() ドキュメント を確認すると、stop_on_slope というパラメーターがあり、デフォルト値は false です。\n設定値が true の場合、重力を考慮した線形速度を適用した状態でオブジェクトが静止している場合、傾斜面でも滑りません。\nこのように、移動方法を次のように変更できます。\nmove_and_slide() これで斜面を滑り落ちるのを止められます！\nしかし依然として問題が残っています。これはgravityに低い値を設定した場合により顕著になります。\n停止時にわずかに上向きの運動量が生じるため、小さな「ホップ」が発生します。この問題は、move_and_slide_with_snap() メソッドに切り替えることで解決できます。\nJump 機能を確実に動作させるため、ジャンプ中のスナップ機能も無効にしてください。そうしないと、プレイヤーは地面にしっかりと「固定」されたままになってしまいます。\nvar snap = Vector3.DOWN if not jumping else Vector3.ZERO move_and_slide() これで「ホップ」がなくなり、すべてが期待通りに動作するようになりました。\n最終的に、非常に急勾配な斜面では、依然として問題が残ることに気づかれるかもしれません：\nこれは、デフォルトの floor_max_angle パラメーター値が45度に設定されており、表示される傾斜角がこの値を超えているためです。この値を超える角度は床として認識されません。値を大きくすると、この傾斜も他の傾斜と同様に扱われるようになります。\nmove_and_slide() 関連レシピ はじめてのGodot : 3D入門 CharacterBody3Dで動かす この動画が気に入ったら？ ","description":"","tags":[],"title":"CharacterBody3D : 坂道で停止する","uri":"/godot_recipes/4.x/ja/physics/kinematicbody_slopes/index.html"},{"content":"ノードはGodotでゲームを作成するための基本構成要素です。ノードとは、特定の種類のゲーム機能を表現するオブジェクトのことです。例えば、グラフィックを表示したり、アニメーションを再生したり、3Dモデルオブジェクトを表現したりできます。各ノードにはカスタマイズ可能なプロパティセットが含まれており、その動作を自由に調整できます。プロジェクトにどのノードを追加するかは、必要な機能性によって異なります。これは、ゲームオブジェクト構築において最大限の柔軟性を提供するよう設計されたモジュールシステムです。\nノード操作について ノードはプログラミング用語でいうところの オブジェクト です。データと動作をカプセル化し、他のノードからプロパティを継承できます。デフォルトの提案機能を使う代わりに、シーンドックにある「子ノードを追加」ボタンをクリックします。\nここでは、エンジンで利用可能なノードタイプの完全な階層構造を確認できます。例えば、青みがかったアイコンを持つノードはすべて「Node2D」カテゴリに属しており、いずれもNode2Dとしてのプロパティを備えています。その詳細については後ほど説明します。\nリストが長く、必要なノードを探すたびに詳細を表示させるのは面倒です。代わりに検索機能を使えば、少ない文字入力で目的の項目を素早く見つけられます。ここでは Sprite2D ノードを探しているので、「sp」と入力するだけですぐにヒットします。「作成」をクリックして、ノードを追加してください。\nこれでシーンドックにこの Sprite2D ノードが追加されました。必ず選択状態にしてください。次に右側のインスペクタードックを見てみてください。ここには現在選択されているノードのすべてのプロパティが表示されます。これらのプロパティは、それらがどのソースから来ているかによって整理されていることに注目してください。Sprite2Dノードは Node2Dを継承しており、これはさらにCanvasItemを継承し、さらにその根本には単純なNodeがあります。\nビューポート上で確認すると、スプライトの表示は期待通りではありません。スプライトの目的は画像（テクスチャ）を表示することだからです。インスペクターを見ると、Textureプロパティが現在空になっているのがわかります。幸いなことに、新規プロジェクトには必ずGodotアイコンが付属しています。このアイコンをファイルシステムドックからドラッグし、テクスチャプロパティにドロップしてください。\nインスペクターで「Transform」セクションを展開するためにクリックし、Position プロパティに (50, 50) と入力してください。\nビューポート内でスプライトをクリック＆ドラッグすることも可能で、移動に合わせてPositionの値が変化していきます。\nノードの重要な特性の一つに、親子関係による階層構造を構築できる点があります。必ずSprite2Dを選択した状態で再度追加ボタンをクリックし、別のSprite2Dを追加してください。さらに、アイコンをテクスチャ領域にドラッグ＆ドロップして配置することも忘れずに行ってください。\nこの新しいスプライトは最初のスプライトの子要素です。これは「親要素に紐付けられている」ことを意味します。親が移動すれば、その子も連動して動きます。まず子スプライトをクリックして、その位置を (50, 50) に設定してください。その後、親スプライトをクリック＆ドラッグすることで、画面上で自由に動かせるようになります。\n親要素の位置を移動させると、位置プロパティが変化していくのに注目してください。次に子要素を確認すると、依然として (50, 50) のままです。これはその子要素の「トランスフォーム」プロパティが、親要素に対して相対的に設定されているためです。\n場面設定 グループ化というこの機能は非常に強力で、ノードを「構成要素」として組み合わせることで複雑なオブジェクトを構築できます。例えば、ゲームの「プレイヤー」ノードには、表示用のSprite2D、アニメーション制御用のAnimationPlayer、追従用カメラとしてのCamera2Dなど、複数の子ノードが追従していることがあります。\nこのように「ツリー」構造に配置されたノード群を、我々は「シーン」と呼んでいます。次のセクションでは、ゲームオブジェクトを独立した管理可能なパーツとして整理し、それらが相互に連携する仕組みについて詳しく解説します。後のレッスンで紹介する実践的な例を通して、この概念を実際に体験していただけるでしょう。\n","description":"","tags":null,"title":"Godotノード's building blocks","uri":"/godot_recipes/4.x/ja/g101/start/101_03/index.html"},{"content":"課題 リジッドボディを目標位置へ移動させたい。\n解決策 RigidBody2D の操作は少し複雑です。Godot の物理エンジンで制御されるため、直接移動させるのではなく、力を加える必要があります。リジッドボディを扱う前に、RigidBody2D API ドキュメント を読むことを強くオススメします。\n物体を移動させるには、特定の方向に力を加える必要があります - これが「力」です。物体が動き始めたら、最終位置に近づくにつれてこの力は徐々に小さくなるようにします。\nこの場合、まさにVector2.distance_to()関数を使用するのに最適な状況です。この関数を使えばターゲットまでの距離を正確に測定でき、その値を基に適切な力を加えることができます。\n# Smoothly move to target extends RigidBody2D var linear_force = 5 var target = position func _physics_process(delta): var dist = position.distance_to(target) constant_force = dir * linear_force * dist 線形ダンピングを使用 デフォルト設定の RigidBody2D でこの操作を試みると、物体が目標を通り過ぎてしまうことがわかります。これはオブジェクトのLinear/Damp プロパティによるものです（デフォルト値は 1 のプロジェクト設定にあります）。この値は「摩擦」を表しており、力を加えない場合に可動式リジッドボディがどのくらいの速さで停止するかを制御します。この値を大きくすると、物体が目標地点でスムーズに減速するようになります。この値と linear_force がどのように相互作用するかを調整することで、まさに求めている動きを実現できます。\n関連レシピ RigidBody2D: ターゲットに向ける ","description":"","tags":null,"title":"RigidBody2D で目標位置へ移動","uri":"/godot_recipes/4.x/ja/physics/smooth_rigid_move/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 画像のグレースケール変換に使用するシェーダーが必要です。\n解決策 まずは canvas_item（2D）用シェーダーから始めてください。グレースケールに変換しつつピクセルのコントラストを維持するには、画素値を平均化が必要です。カラーチャンネルをすべて加算し、3で割ることで実現できます。\nshader_type canvas_item; void fragment() { COLOR = texture(TEXTURE, UV); float avg = (COLOR.r + COLOR.g + COLOR.b) / 3.0; COLOR.rgb = vec3(avg); } この機能を画面全体に適用するには、ColorRect コンポーネント（カメラの動きを無視するため CanvasLayer 内に配置）を追加し、画面全体を覆うようにスケールを調整してください。\ntexture() 関数を修正し、オブジェクトのピクセルではなく画面を直接サンプリングするようにしてください。\nCOLOR = texture(SCREEN_TEXTURE, SCREEN_UV); 関連するレシピ シェーダー入門 ","description":"","tags":null,"title":"グレースケール（モノクロ）シェーダー","uri":"/godot_recipes/4.x/ja/shaders/greyscale/index.html"},{"content":"最後のセクションでは、プロジェクトを設定し、ゲームアートをダウンロードしました。これでコーディングを開始する準備が整いました。まずはプレイヤー操作可能な宇宙船から着手します。\nスクリプトの追加方法 ゲームの動作やメカニクスを構築するには、スクリプトを記述してノードやその他のオブジェクトにアタッチします。現在のPlayerシーンでは宇宙船を表示し、その衝突判定ボックスなどを定義していますが、実際には移動できず、仮に他のものと衝突しても何の反応も生じません。この機能を追加するため、これからコードを実装していきます。\nPlayerノードを選択して、「スクリプトのアタッチ」ボタンをクリックします。\nノードスクリプトのアタッチ ウィンドウでオプションを変更する必要はありませんので、単に 作成 ボタンをクリックすると、スクリプトエディターに移動します。\nスクリプトの最初の行を見てみてください。これは自動的に追加されたものです。\nextends Area2D この行はこのスクリプトをアタッチすべきオブジェクトのタイプを指定しています。つまり、スクリプトは Area2D が提供するすべての機能にアクセスできるようになります。\nスクリプトが『組み込まれている』ノードの種類と、extend 文は常に一致している必要があります。\nスクリプトの操作について 単独で存在するスクリプト自体には、ほとんど意味がありません。スクリプトは付加的な機能を定義するものであり、特定のオブジェクトに紐づいて初めて効果を発揮します。単に「あるスクリプト内の変数」ではなく、そのスクリプトによって定義されたオブジェクトのプロパティを参照することになる点が重要です。これは極めて重要な概念です。\n移動 まず、船を画面上で移動させる機能から作成してください。以下の動作するコードから始めてみます。\nプレイヤーが押している入力を検知する その入力方向に宇宙船を移動させる @export var speed = 150 func _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") position += input * speed * delta これを行ごとに分解してみます。\n変数名の前に @export を付けると、インスペクター でその値を調整できるようになります。 _process() 関数はエンジンによって1フレームごとに呼び出されます。この関数内に書いたコードはすべて毎フレーム実行されます。 Input.get_vector() は、指定された4つの入力状態をチェックし、それらの方向に向くベクトルを生成します。 最後に、与えられた入力ベクトルを移動量として船の position に加算します。この時、速度値に合わせてベクトルの大きさを調整し、さらに delta でスケール処理を行います。 詳細情報へのリンク ベクトルの基本理解：ベクトル数学 deltaとは？ デルタについての解説 シーンを実行するには ［現在のシーンを実行］ ボタンをクリックしてください。その後、自由に移動してみてください。\n画面表示を維持する機能 一つの問題点として、プレイヤーが動き続けると画面外へと移動してしまう点が挙げられます。この問題を解決するには、スクリプト上部でプレイヤーの position プロパティを画面矩形内に制限が必要です。以下のように追加してください。\n@onready var screensize = get_viewport_rect().size ここでの @onready は、Godotが Playerノードがシーンツリーに加わるまで screensize変数の値を設定しないように指示しています。本質的には「ゲーム開始を待つ」ということです。なぜなら、ゲームが実行されていない状態ではウィンドウの大きさを取得する方法がないからです。\n次のステップは、位置をその screensize 矩形の範囲内で固定することです。position が使用する Vector2 には、clamp() メソッドがあります。position 設定直後に、この行を追加してください。\nfunc _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") position += input * speed * delta position = position.clamp(Vector2.ZERO, screensize) もう一度シーンを実行して、画面の端から移動してみてください。船の半分が画面からはみ出していることに気付くはずです。これは、船の position 値が Sprite2D オブジェクトの中心座標に設定されているためです。船のサイズは 16x16 ピクセルであることがわかっているため、clamp() 関数に追加で8ピクセル分を包含するように変更できます。\nposition = position.clamp(Vector2(8, 8), screensize - Vector2(8, 8)) 方向に合わせたアニメーションの適用方法 船が動いている状態では、左または右に移動する際に「傾斜した」船の画像を選択できるほか、対応する『ブースター』アニメーションも表示されます。\n移動方向を判断するには、inputベクトルのx値を確認します。値が正であれば右方向へ、負なら左方向へ、0であれば停止中と判定し、それぞれAnimatedSprite2Dの対応するframe値を持つSprite2Dと、適切なanimationを選択します。\nfunc _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") if input.x \u003e 0: $Ship.frame = 2 $Ship/Boosters.animation = \"right\" elif input.x \u003c 0: $Ship.frame = 0 $Ship/Boosters.animation = \"left\" else: $Ship.frame = 1 $Ship/Boosters.animation = \"forward\" position += input * speed * delta position = position.clamp(Vector2(8, 8), screensize-Vector2(8, 8)) もう一度シーンを再生し、左右へ移動した際に画像が正しく切り替わることを確認してください。次のステップに進む前に、すべてが意図通りに動作することを必ず検証してください。\n次のステップでは、Bullet シーンを作成し、プレイヤーが射撃できるようにします。\n戻る 次へ ","description":"","tags":null,"title":"プレイヤーのコーディング","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_03/index.html"},{"content":"課題 マウスカーソルを隠し、ゲームウィンドウからマウス。これは多くの3Dゲーム（および一部の2Dゲーム）で一般的な機能です。\n解決策 マウスの状態を設定したい場合は、Input.mouse_modeを使用できます。利用可能なマウスモードは以下の4種類です。\nMOUSE_MODE_VISIBLE: マウスが視認可能で、ウィンドウ内外を自由に移動できます。これがデフォルト状態です。\nMOUSE_MODE_HIDDEN：マウスカーソルは表示されませんが、マウス操作でウィンドウ外に移動できます。\nMOUSE_MODE_CAPTURED：マウスカーソルが非表示になり、ゲームウィンドウの外側にマウスを移動させることができなくなります。\nMOUSE_MODE_CONFINED ：マウスは表示されていますが、ゲームウィンドウの外には移動できません。\n「キャプチャ」は最も一般的に使用されるオプションです。実行時にマウスモードを設定するには、以下のコマンドを使用できます。\nfunc _ready(): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED マウスが捕捉されている状態でも、通常どおりマウス入力イベントは伝達されます。ただし、問題が生じることに注意してください。ゲームを終了したり他のウィンドウに切り替えたい場合、それが不可能になります。このため、「マウス解放」機能も実装しておくと便利です。例えば、プレイヤーがEscキーを押したときにマウスを解放するには。\nfunc _input(event): if event.is_action_pressed(\"ui_cancel\"): Input.mouse_mode = Input.MOUSE_MODE_VISIBLE 他のウィンドウに表示されている時にマウス操作がゲームに反映されないようにするには、キャラクターコントローラ内で以下を使用してキャプチャ状態を判定できます。\nif Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: マウスボタンを離すと、再びクリック操作を開始するために再キャプチャが必要になります。インプットマップにマウスクリック用のイベントが設定されている場合、以下のように対処できます。\nif event.is_action_pressed(\"click\"): if Input.mouse_mode == Input.MOUSE_MODE_VISIBLE: Input.mouse_mode = Input.MOUSE_MODE_CAPTURED マウスクリックで射撃や別のアクションを実行する場合もあるため、イベント伝播を停止させるのは有効な方法です。マウスモード設定後に以下を追加してください。\nget_tree().set_input_as_handled() ","description":"","tags":null,"title":"マウスのキャプチャ","uri":"/godot_recipes/4.x/ja/input/mouse_capture/index.html"},{"content":"課題 FPSゲームで射撃機能を実装しますが、個別の発射物を手動で動かすのは非現実的です。\n解決策 ゲームの物理演算エンジンは、非常に高速で移動するオブジェクトを処理しようとすると破綻しがちです。解決策としては、射手の位置からレイキャストを行い、最初に衝突する物体を検出する方法が有効です。\nGodotにおけるレイキャスティングには、主に2つの方法があります。RayCast3Dノードを使用する方法と、物理エンジンを直接操作して空間内に直線状の レイ を投射する方法です。どちらも同じ目的を達成できますが、それぞれに異なる利点があります。ノードベースのアプローチは、継続的な衝突検出が必要な場合に特に有効です - 例えば、床に触れているかどうかを確認するための下向きのレイを継続的にチェックする場合などに最適です。\n2つ目の方法、つまり物理状態を問い合わせる方法を採用します。これは、「発射」キーを押した瞬間に、何かに当たったかどうかを知りたいからです。\nメモ このレシピでは、すでに動作するFPSキャラクターコントローラーと移動可能なワールドが用意されていることを前提としています。もし用意されていない場合は、先にFPSキャラクターの作り方のチュートリアルを参照してください。\nヒットした内容を確認するには、FPSPlayerシーンにCanvasLayerノードとLabelノードを追加してください。\nマウス入力を処理するために既に使用している _input() 関数に、入力チェックを追加してください。\nif event.is_action_pressed(\"shoot\"): shoot() 次に、shoot() メソッドを定義します。この関数が呼び出されるたびに、以下の処理を実行する PhysicsRayQueryParameters3D オブジェクトを作成します。\nレイの始点（カメラ位置）を定義 レイの終点（カメラから前方100メートル投影された位置）を定義 このパラメータはワールドの direct_space_state を通じて物理エンジンに渡されます。衝突情報を含むDictonaryが返された場合、そのデータに基づいてラベルを更新し、当たったオブジェクトの種類を表示できるようにします。\nfunc shoot(): var space = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.create($Camera3D.global_position, $Camera3D.global_position - $Camera3D.global_transform.basis.z * 100) var collision = space.intersect_ray(query) if collision: $CanvasLayer/Label.text = collision.collider.name else: $CanvasLayer/Label.text = \"\" 関連レシピ FPSキャラクターの作り方 プロジェクトのダウンロード プロジェクトコードはこちらよりダウンロードできます。 https://github.com/godotrecipes/3d_shoot_castrays\n","description":"","tags":[],"title":"レイキャストを使用した射撃","uri":"/godot_recipes/4.x/ja/3d/shooting_raycasts/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 剣やパンチなどの近接攻撃を実装したい。\n解決策 本例では、既に攻撃アニメーションが設定されたキャラクターが存在すると仮定します。説明のために、以下の2種類の攻撃を使用します。\nArea2D を使用して剣がターゲットに命中したことを検出できますが、実際に有効化する必要があるのはスイング動作中のみです。アニメーションと同期させるためには、この活性化をAnimationPlayerで制御します。\nシーンに Area2D と CollisionShape2D を追加してください。ヒットボックスには矩形形状を使用し、剣が振り上げフレームで完全にカバーされるようにサイズを調整します。\nアニメーションを最初のフレームに移動し、領域の [無効] プロパティを確認します。キーフレームアイコンをクリックしてアニメーションにトラックを追加してください。次に、剣が伸びているフレームまでアニメーションを進め、[無効] を解除した状態でもう1つのキーフレームを追加してください。最後に、スイングの終わりまで進み、再度 [無効] を有効にしてキーフレームを作成します。\n新しいエリアのarea_enteredシグナル（またはゲームの設定によっては、body_entered）を接続します。このデモでは、ダメージを受け取れる任意のボディにはArea2Dが定義され、「hurtbox」というグループ内に配置されているとします。\nfunc _on_SwordHit_area_entered(area): if area.is_in_group(\"hurtbox\"): area.take_damage() これで実際に試してみて、ターゲットが剣の当たり判定範囲内にいればダメージが発生するか確認できるはずです。\nヒットボックスサイズの変更方法 複数の攻撃アニメーションがある場合、ダメージ範囲の大きさが統一されていないことがあります。上記のアニメーション例では、最初の動きは広範囲をカバーする斜め上方向への振り下ろし攻撃です。この処理に対応するため、衝突形状の Extents プロパティに対するアニメーショントラックも追加が必要です。この値を設定して各アニメーションの開始時にキーフレームとして登録してください。\n関連レシピ 見下ろし型キャラクター制御 アニメーション状態の管理方法 この動画が気に入ったら？ ","description":"","tags":null,"title":"近接攻撃","uri":"/godot_recipes/4.x/ja/animation/melee_attacks/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n最初の2部で基本ゲームプレイは完成しました。ここからは、サークルに様々なモードを追加していきます。\nサークルモードについて 最終的には様々なモードを実装する予定ですが、まずは「制限モード」から始めてください。このモードでは、円は特定回数の周回後に消滅します。まず、残りの周回数を表示する Label ノードを追加してください。テキストフィールドに数値（例：1）を入力して、表示方法を確認してみてください。\n[カスタムフォント] セクションで、新しい DynamicFont を追加し、アセットフォルダから フォントデータ を読み込み、 サイズ を 64 に設定します。ラベルの位置を中央配置するには、「レイアウト」メニューから「中央揃え」を選択します。\n以下の新しい変数を Circle.gd ファイルの先頭に追加してください。\nenum MODES {STATIC, LIMITED} var mode = MODES.STATIC var num_orbits = 3 # Number of orbits until the circle disappears var current_orbits = 0 # Number of orbits the jumper has completed var orbit_start = null # Where the orbits started 次に、モードを設定する方法が必要です。\nfunc set_mode(_mode): mode = _mode match mode: MODES.STATIC: $Label.hide() MODES.LIMITED: current_orbits = num_orbits $Label.text = str(orbits_left) $Label.show() 現在はこれら2つのモードが定義されていますが、後でさらに追加される予定です。\nさらに、init() メソッドにモードを引数として渡す方法も追加してください。デフォルト値は STATIC のままにしますが、今回はテスト用に LIMITED を使用することにします。これにより、以下のことが可能になります。\nfunc init(_position, _radius=radius, _mode=MODES.LIMITED): set_mode(_mode) The jumper is setting the rotation position when it’s captured. Remove the line from Jumper.gd and put it in the circle’s capture() method:\nfunc capture(target): jumper = target $AnimationPlayer.play(\"capture\") $Pivot.rotation = (jumper.position - position).angle() orbit_start = $Pivot.rotation 注意：現在はジャンパーへの参照を渡しているため、スクリプトの先頭にvar jumper = nullを追加し、Main.gdスクリプト内の呼び出しをobject.capture(player)に変更してください。\n現在位置が一周したか確認し、もしそうであればcurrent_orbitsを1減算します。\nfunc _process(delta): $Pivot.rotation += rotation_speed * delta if mode == MODES.LIMITED and jumper: check_orbits() func check_orbits(): # Check if the jumper completed a full circle if abs($Pivot.rotation - orbit_start) \u003e 2 * PI: current_orbits -= 1 $Label.text = str(current_orbits) if orbits_left \u003c= 0: jumper.die() jumper = null implode() orbit_start = $Pivot.rotation この機能を動作させるには、ジャンパークラスにdie()メソッドを追加が必要です。\nfunc die(): target = null queue_free() func _on_VisibilityNotifier2D_screen_exited(): if !target: die() さらに、ジャンパの VisibilityNotifier2D シグナルも接続しました。これにより、プレイヤーが画面外に出た際にプレイヤーオブジェクトを削除できるようになっています。\n試しに動かしてみると、今のところ問題なく動作しています。\nサークル効果 このセクションの最後の作業として、軌道が尽きつつあることを示すために円に「塗り」効果を追加してください。まず、公式ドキュメントにある描画コードを使用します。\nfunc draw_circle_arc_poly(center, radius, angle_from, angle_to, color): var nb_points = 32 var points_arc = PoolVector2Array() points_arc.push_back(center) var colors = PoolColorArray([color]) for i in range(nb_points + 1): var angle_point = angle_from + i * (angle_to - angle_from) / nb_points - PI/2 points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius) draw_polygon(points_arc, colors) 以下の関数は _draw() 内で呼び出されます。\nfunc _draw(): if jumper: var r = ((radius - 50) / num_orbits) * (1 + num_orbits - current_orbits) draw_circle_arc_poly(Vector2.ZERO, r, orbit_start + PI/2, $Pivot.rotation + PI/2, Color(1, 0, 0)) 最後に、_physics_process に update() を追加し、check_orbits() が呼び出されるたびに実行されるようにしてください。\n次のパートでは、UI要素を追加していきます。\nGitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\n動画で見る ","description":"","tags":null,"title":"限定サークル","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_03/index.html"},{"content":"課題 実行時にインプットマップにアクションを追加が必要です。\n解決策 通常、入力アクションは[プロジェクト設定]から追加します（詳細はレシピ : 入力アクション参照）。ただし、スクリプト内で直接アクションを追加する必要がある場合もあります。このような場合には、InputMapシングルトンが提供するメソッドが便利です。\n以下に、スペースキーを使用して「攻撃」という新しいアクションを追加する例を示します。\nfunc _ready(): InputMap.add_action(\"attack\") var ev = InputEventKey.new() ev.keycode = KEY_SPACE InputMap.action_add_event(\"attack\", ev) 左マウスボタンも同じ操作に追加したい場合は。\nev = InputEventMouseButton.new() ev.button_index = MOUSE_BUTTON_LEFT InputMap.action_add_event(\"attack\", ev) メモ InputMap.add_action() メソッドは、同じアクションが既に存在する場合にエラーを発生させます。新しいアクションを追加する前に、まず InputMap.has_action() で確認することをオススメします。\n実際の使用例 たとえば、レシピ: プラットフォームキャラクターで作成したプラットフォームキャラクターを別のプロジェクトで再利用したい場合を考えてみます。シーンファイル、スクリプト、およびアセットをすべて単一フォルダに保存していれば、そのフォルダ全体を新規プロジェクトにコピーするだけで済みます。ただし、入力が正常に機能するようにするには、インタラクションマップも編集が必要です。\n代わりに、以下のコードをプレイヤースクリプトに追加すれば、必要な入力アクションが自動的に追加されるようになります。\nvar controls = {\"walk_right\": [KEY_RIGHT, KEY_D], \"walk_left\": [KEY_LEFT, KEY_A], \"jump\": [KEY_UP, KEY_W, KEY_SPACE]} func _ready(): add_inputs() func add_inputs(): var ev for action in controls: if not InputMap.has_action(action): InputMap.add_action(action) for key in controls[action]: ev = InputEventKey.new() ev.keycode = key InputMap.action_add_event(action, ev) 関連レシピ 入力アクション設定 プラットフォーマーキャラクタの作成 ","description":"","tags":null,"title":"実行中に入力アクションを追加する","uri":"/godot_recipes/4.x/ja/input/custom_actions/index.html"},{"content":"課題 2D見下ろし方式カーコントローラーを作成したい。\n解決策 この問題に取り組む際、初心者はしばしば実際の車とはかけ離れた挙動のゲームを作ってしまいがちです。アマチュアが作ったカーゲームでよく見られる失敗例をご紹介します。\n自動車は中心軸を中心に旋回するものではありません。別の言い方をすれば、後輪が左右に滑ることはありません。（ドリフト走行時はこの限りではないが、その点については後述します） 自動車は動いている時にのみ方向転換が可能で、その場で回転することはできません。 自動車は列車ではありません。レールに縛られていません。高速で曲がる際にはある程度の横滑り（ドリフト）が伴うべきです。 2Dカー物理の実装には様々なアプローチ方法があり、主に「リアル志向」か「アーケードスタイル」かによって選択が分かれます。ここでは、リアリティよりもアクション性を優先する「アーケードレベル」の現実感を追求していきます。\nメモ 以下の方法は、こちらのアルゴリズムに基づいています。 https://engineeringdotnet.blogspot.com/2010/04/simple-2d-car-physics-in-games.html\n以下のレシピは5つのセクションに分かれており、それぞれが車の異なる動作要素を追加してください。必要に応じて自由に組み合わせてご使用ください。\nシーン設定 以下が車シーンの設定内容です。\nCharacterBody2D Sprite2D CollisionShape2D Camera2D このデモでは、Kenneyのレーシングパックのアートワークを使用します。CapsuleShape2Dは衝突判定に最適な形状です。これにより、車が障害物に引っかかるような鋭い角を防ぐことができます。\n以下の4つの入力操作も使用します。「右旋回」「左旋回」「加速」「ブレーキ」。お好みのキー割り当てを設定してください。\nその1：動き 最初のステップは、前述のアルゴリズムに基づいて動作をコーディングすることです。\n以下の変数から始めてください。\nextends CharacterBody2D var wheel_base = 70 # Distance from front to rear wheel var steering_angle = 15 # Amount that front wheel turns, in degrees var steer_direction wheelbase をスプライトに適した値に設定してください。\nsteer_direction には車輪の回転量が設定されます。\nメモ キーボード操作を使用している場合、ターンはオン／オフの二択になります。アナログジョイスティックを使用する場合は、代わりにスティックの移動距離に応じてこの値を調整できます。\nfunc _physics_process(delta): get_input() calculate_steering(delta) move_and_slide() 各フレームでは、入力のチェックとステアリング計算が必要です。その後、算出された速度を move_and_slide() 関数に渡します。これら2つの関数については次に定義します。\nfunc get_input(): var turn = Input.get_axis(\"steer_left\", \"steer_right\") steer_direction = turn * deg_to_rad(steering_angle) velocity = Vector2.ZERO if Input.is_action_pressed(\"accelerate\"): velocity = transform.x * 500 ここではユーザー入力を確認し、移動速度を設定します。注意：速度500は一時的なもので、動きのテスト用です。これについては次のセクションで対処します。\n以下に、リンク先のアルゴリズムを実装します。\nfunc calculate_steering(delta): # 1. Find the wheel positions var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 # 2. Move the wheels forward rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_direction) * delta # 3. Find the new direction vector var new_heading = rear_wheel.direction_to(front_wheel) # 4. Set the velocity and rotation to the new direction velocity = new_heading * velocity.length() rotation = new_heading.angle() プロジェクトを実行すると、車が動き始め、方向転換するはずです。ただし現在の動作はまだ不自然で、車の動きが瞬時に開始・停止します。これを改善するため、計算に加速度を追加してください。\nその2：加速について 以下の設定変数と、車全体の加速を追跡するための変数が必要になります。\nvar engine_power = 900 # Forward acceleration force. var acceleration = Vector2.ZERO 入力コードを変更して、車の velocity を直接変更するのではなく、加速度を適用するようにします。\nfunc get_input(): var turn = Input.get_axis(\"steer_left\", \"steer_right\") steer_direction = turn * deg_to_rad(steering_angle) if Input.is_action_pressed(\"accelerate\"): acceleration = transform.x * engine_power 一旦加速度が得られたら、以下のように速度に適用できます。\nfunc _physics_process(delta): acceleration = Vector2.ZERO get_input() calculate_steering(delta) velocity += acceleration * delta move_and_slide() 今から車を走らせると、徐々に速度が上がっていきます。注意：まだ減速する方法はないんですよ！\nその3：摩擦・抵抗について 自動車には2種類の異なる減速力が作用します。摩擦力と空気抵抗です。\n摩擦力は路面が車に及ぼす抵抗力です。砂地では非常に強いですが、氷上では極めて小さくなります。この力は速度に比例し、スピードが速いほど大きくなります。\nドラッグは空気抵抗によって生じる力です。車両の断面積が大きいほど影響が大きくなります。例えば、流線型のレースカーに比べて大型トラックやバンの方がより大きな抗力を受けます。ドラッグの値は速度の二乗に比例します。\nこれにより、低速走行時には摩擦抵抗がより顕著になる一方、高速域では空気抵抗が支配的になります。これら両方の力を計算に含めます。さらに、これらの量の値は、車の最高速度――エンジン出力がもはや空気抵抗に打ち勝てなくなる点――を示す指標にもなります。\n以下にこれらの数量に対する初期値を示します。\nvar friction = -55 var drag = -0.06 このグラフから分かるように、これらの値は、速度が600に達した時点で、抗力が摩擦力を上回ることを示しています。\nこちらのツールで値を変更してその影響を確認できます。 https://www.desmos.com/calculator/e4ayu3xkip\n関数 _physics_process() では、現在の摩擦係数を計算する関数を呼び出し、それを加速度力に適用します。\nfunc _physics_process(delta): acceleration = Vector2.ZERO get_input() apply_friction(delta) calculate_steering(delta) velocity += acceleration * delta velocity = move_and_slide(velocity) func apply_friction(delta): if acceleration == Vector2.ZERO and velocity.length() \u003c 50: velocity = Vector2.ZERO var friction_force = velocity * friction * delta var drag_force = velocity * velocity.length() * drag * delta acceleration += drag_force + friction_force まず、最低速度を設定してください。これにより、摩擦力が完全に車の速度をゼロにさせない場合でも、車両が極端に低速で前進し続けるのを防ぐことができます。\nその後、これら2つの力を計算し、合計加速度に加算します。どちらも負の値なので、車には逆方向に作用になります。\nその4：リバース／ブレーキ操作 以下の 2 つの設定変数も必要です。\nvar braking = -450 var max_speed_reverse = 250 get_input()で入力できるようにしましょう。\nif Input.is_action_pressed(\"brake\"): acceleration = transform.x * braking これは停止時には問題ありませんが、リバースギアに入れたときに正常に動作しない問題があります。現在、加速は常に「進行方向」（前進）に対して適用されているため、バック走行時に逆方向に加速することができません。リバース時は後方への加速度を加える必要があります。\nfunc calculate_steering(delta): var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_angle) * delta var new_heading = (front_wheel - rear_wheel).normalized() var d = new_heading.dot(velocity.normalized()) if d \u003e 0: velocity = new_heading * velocity.length() if d \u003c 0: velocity = -new_heading * min(velocity.length(), max_speed_reverse) rotation = new_heading.angle() ドット積を使用することで、前進または後退の加速状態を判定できます。2つのベクトルが一致していれば、結果は 0 より大きくなります。移動方向が車の向いている方向と逆向きの場合、ドット積は 0 未満となり、後退中であることが分かります。\nその5：ドリフト/スライド操作 ここで止めても十分満足のいく運転体験が得られるでしょう。ただ、車がまだ「レールに乗っている」ような感覚です。最高速で走行していても、カーブは完璧にクリアされ、まるでタイヤが完全に路面を掴んでいるみたいです。\n高速時（あるいは低速でもお好みで）には、駆動力によってタイヤが滑り、車体が「魚の尾のように」左右に滑る動きが生じるはずです。\nvar slip_speed = 400 # Speed where traction is reduced var traction_fast = 2.5 # High-speed traction var traction_slow = 10 # Low-speed traction これらの値は、ステアリング計算時に適用します。現在の設定では、速度が瞬時に新しい進行方向に切り替わるようになっています。代わりに、補間処理（lerp()）を使用して、車両が新しい方向に完全に向きを変えることなく、部分的にしか旋回しないようにします。その際、「トラクション」値がタイヤの「粘着性」を決定します。\nfunc calculate_steering(delta): var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_angle) * delta var new_heading = (front_wheel - rear_wheel).normalized() # choose which traction value to use - at lower speeds, slip should be low var traction = traction_slow if velocity.length() \u003e slip_speed: traction = traction_fast var d = new_heading.dot(velocity.normalized()) if d \u003e 0: velocity = lerp(velocity, new_heading * velocity.length(), traction * delta) if d \u003c 0: velocity = -new_heading * min(velocity.length(), max_speed_reverse) rotation = new_heading.angle() ここでは、使用する牽引値を選択し、velocityにlerp()を適用します。\n調整項目 この時点で、車両の挙動を制御する多数の設定項目があります。これらを調整することで、車の運転特性を大きく変更できます。さまざまな値を試す作業をより簡単にするため、以下にレシピ用プロジェクトをダウンロードしてください。ゲームを起動すると、走行中に車の挙動を変更可能なスライダーパネルが表示されます（\u003cTab\u003eキーでスライダーパネルの表示/非表示を切り替え可能）。\n関連レシピ ゲーム数学：補間 プロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。https://github.com/godotrecipes/2d_car_estrada\n","description":"","tags":null,"title":"車のステアリング操作","uri":"/godot_recipes/4.x/ja/2d/car_steering/index.html"},{"content":"はじめに 衝突レイヤーとマスクは、Godot 4においてどのオブジェクト同士が相互作用するかを制御する上で不可欠な要素です。\nシステム Collision Layer(衝突判定レイヤー)：オブジェクトが 存在する レイヤを指定します。 Collision Mask(衝突検知マスク)：オブジェクトが 衝突を検出する 対象のレイヤを指定します。 サンプルプロジェクトでの設定例 ノード レイヤー マスク 相互作用 プレイヤー 1 2, 3 敵とコインをスキャン 敵キャラ 2 1 プレイヤーをスキャン コイン 3 (なし) 特にスキャンする必要がない セットアップ方法 Godot 4では、プロジェクト設定の［レイヤー名］＞［2D物理］でレイヤーに名前を付けられます。これにより、インスペクターでの管理が大幅に効率化されます。\nノードの選択方法 Godot 4では、ビット操作を容易にするため、get_collision_layer_value(layer_number)とset_collision_layer_value(layer_number, value)を使用することをオススメします。\n# Enable layer 2 set_collision_layer_value(2, true) # Check if masking layer 3 if get_collision_mask_value(3): print(\"Scanning layer 3\") Godot 3から4への移行ポイントまとめ レイヤー用インスペクタUIが改善されました set_collision_layer_value()などの新ヘルパーメソッドにより、複雑なビット演算処理(例: 1 \u003c\u003c (layer - 1))が不要になりました ","description":"","tags":null,"title":"衝突レイヤーとマスク","uri":"/godot_recipes/4.x/ja/physics/collision_layers/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 運動キャラクターに摩擦と加速度を加え、より自然な動きを実現したい。\n解決策 多くのゲームにおいて、必ずしも完全な物理シミュレーションを求めているわけではありません。重要なのはアクション性、反応の良さ、そしてアーケードならではの爽快感です。だからこそ、RigidBodyではなくKinematicBodyを選択するのです - これによって物体の動きを直接制御できるようになります。ただし、ある程度の物理的な挙動は必要です。これはつまり、オブジェクトが突然方向を変えたり停止したりしないようにするためです。\n以下に、シンプルな運動力学に基づくプラットフォーマーキャラクター用のコードを示します。\nextends CharacterBody2D var speed = 1200 var jump_speed = -1800 var gravity = 4000 var velocity = Vector2.ZERO func get_input(): velocity.x = 0 if Input.is_action_pressed(\"ui_right\"): velocity.x += speed if Input.is_action_pressed(\"ui_left\"): velocity.x -= speed func _physics_process(delta): get_input() velocity.y += gravity * delta move_and_slide() if Input.is_action_just_pressed(\"ui_select\"): if is_on_floor(): velocity.y = jump_speed このコードを実行すると、キャラクターのx方向速度が瞬時に変化してしまうことに気づくでしょう。これを修正するために、lerp()関数を使用して速度を徐々に増減させるようにします。\nlerpの使用例 lerp(start_value, end_value, amount) lerp()（線形補間）は、2つの数値間で「ブレンドされた」値を求める関数です。詳細は補間処理を参照してください。\n以下のコードでは、friction(摩擦係数)はキャラクターが停止する速度を、acceleration(加速力)は最大加速するまでの速度を決定する要素です。どちらも値が0.0から1.0の範囲内で設定されます。\nget_input()のコードを以下に置き換えます。\nvar friction = 0.1 var acceleration = 0.5 func get_input(): var input_dir = 0 if Input.is_action_pressed(\"ui_right\"): input_dir += 1 if Input.is_action_pressed(\"ui_left\"): input_dir -= 1 if dir != 0: # accelerate when there's input velocity.x = lerp(velocity.x, dir * speed, acceleration) else: # slow down when there's no input velocity.x = lerp(velocity.x, 0, friction) 解説 friction(摩擦係数)とacceleration(加速力)をブレンド量として使用します。speedには、現在速度から最大速度までの適切な値を求めます。減速時には、現在速度を徐々に0へ減衰させます。\nヒント 値に 1.0 を使用すると、当初の「瞬間的な」移動状態が復活します。\n関連するレシピ プラットフォームキャラクター ","description":"","tags":null,"title":"動摩擦","uri":"/godot_recipes/4.x/ja/physics/kinematic_friction/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 このdeltaパラメーター（delta time）は、ゲーム開発において誤解されがちな概念です。本チュートリアルでは、どのように使用されるのか、フレームレートに依存しない移動の重要性、そしてGodotにおける実践的な使用例について解説します。\n解決策 問題を具体的に説明するため、画面上を移動するSpriteノードを考えてみます。画面幅が 600 ピクセルで、スプライトがこのスクリーン全体を横切るのに 5 秒かかる場合、必要な移動速度は以下の計算で求められます。\n600 pixels / 5 seconds = 120 pixels/second _process()関数を使用して各フレームでスプライトを移動させます。ゲームが 60 フレーム/秒で動作している場合、フレームごとの移動量は次のように計算できます：\n120 pixels/second * 1/60 second/frame = 2 pixels/frame ヒント 上記のすべての計算において、単位が統一されていることに注目してください。常に計算式で使用する単位に注意を払います。これにより、ミスを防ぐことができます。\n以下に必要なコードを示します。\nextends Node2D # Desired movement in pixels/frame var movement = Vector2(2, 0) func _process(delta): $Sprite.position += movement このコードを実行すると、スプライトが画面を横切るのに5秒かかることがわかります。\nこのコードだと、コンピュータが他のタスクでリソースを消費している場合に問題が発生します。これは「ラグ」と呼ばれます。コード自体の問題や他の実行中アプリケーションに起因する可能性があります。この状況では、フレームの長さが増加します。極端な例として、フレームレートが半減した場合を考えてみます。各フレームの処理時間が60分の1秒から30分の1秒になります。2 ピクセル/フレームで移動しているスプライトの場合、画面端に到達するまでにこれまでの2倍の時間がかかることになります。\nたとえ微小なフレームレートの変動があったとしても、動きの速さが一定に保たれなければなりません。これが銃弾など高速で移動する物体であれば、速度が落ちるのを避けたいです。この移動動作を フレームレートに依存しない ようにする必要があります。\nフレームレート問題の修正について _process()関数を使用する場合、自動的にエンジンから渡されるパラメーターとしてdeltaが含まれます（これは_physics_process()と同様で、物理関連コード用に使います）。これは時間間隔を表す浮動小数点数値であり、直前のフレームから経過した時間の長さを示します。通常この値は1/60秒、つまり約0.0167秒に相当します。\nこの情報があれば、各フレームの移動量を考える必要がなくなり、希望するピクセル単位速度（上記計算結果の120）のみを考慮すれば済むようになります。\nエンジンの delta 値にこの数値を掛けることで、各フレームでピクセルを移動する量が決定されます。フレーム時間が変動した場合でも自動的に調整されるため、手動での設定は不要です。\n# 60 frames/second 120 pixels/second * 1/60 second/frame = 2 pixels/frame # 30 frames/second 120 pixels/second * 1/30 second/frame = 4 pixels/frame 注：フレームレートが半分に低下した場合（すなわちフレーム時間が2倍になった場合）、速度を維持するには、フレームごとの移動量も2倍にする必要があります。\nこの計算を使用するようにコードを変更してください。\nextends Node2D # Desired movement in pixels/second. var movement = Vector2(120, 0) func _process(delta): $Sprite.position += movement * delta 現在毎秒 30 フレームで動作させている場合、移動時間は以下のように一定に保たれています。\nフレームレートが著しく低下した場合、動きは滑らかさを失いますが、時間間隔自体は維持されます。\nデルタを運動方程式と併用する方法 動きがより複雑になったらどうしますか？基本的な考え方は同じです。単位は常に秒を使用し、フレームは使用しないようにし、各フレームごとに delta で乗算してください。\nヒント ピクセル単位や秒単位で考えると、現実世界での測定方法に直結するため直感的にも理解しやすいですよね。「重力加速度は 毎秒100ピクセル/秒 なので、ボールが2秒後には毎秒200ピクセルの速さになっている」といった具合です。フレーム単位で扱う場合は、加速を ピクセル/フレーム/フレーム という単位で計算しなければなりません。実際に試してみてください…あまり自然ではありませんよ。\nたとえば、重力を適用している場合、これは加速度です。各フレームごとに速度に一定の値が加算されます。上記の例と同様に、これによりノードの位置が変化します。\n以下のコードでdeltaとtarget_fpsを調整してみると効果が確認できます。\nextends Node2D # Acceleration in pixels/sec/sec. var gravity = Vector2(0, 120) # Acceleration in pixels/frame/frame. var gravity_frame = Vector2(0, .033) # Velocity in pixels/sec or pixels/frame. var velocity = Vector2.ZERO var use_delta = false var target_fps = 60 func _ready(): Engine.target_fps = target_fps func _process(delta): if use_delta: velocity += gravity * delta $Sprite.position += velocity * delta else: velocity += gravity_frame $Sprite.position += velocity フレームごとに時間ステップで velocity(速度) と position(位置) を更新している点に注意してください 各フレームで更新される量については、フレームレートに依存せず適切に変化させるため、必ず delta を掛けてください。\n運動関数の活用について 上記の例では簡略化のためSpriteを使用していますが、実際には2D/3D空間における移動ボディを使用する場合、それぞれに適した移動メソッドを使用します。またmove_and_slide()関数については、速度ベクトルを扱うため若干混乱が生じやすい点に注意が必要で、距離計算のために速度にdeltaを掛ける必要はありません。これは関数側で自動的に処理されます。ただし、加速度などの他の計算には依然としてdeltaを掛ける必要があります。\n# Sprite movement code: velocity += gravity * delta position += velocity * delta # Kinematic body movement code: velocity += gravity * delta move_and_slide() 加速を適用する際に delta を考慮しない場合、フレームレートの変動の影響を受けやすくなります。この影響は運動挙動に「より微妙な形」で現れます。動きが一貫しなくなりますが、影響が微細であるため、原因の特定は非常に困難になります。\nヒント move_and_slide()関数を使用する場合でも、重力や摩擦などの他の物理量に対しても適切にdeltaを適用が必要です。\n関連するレシピ ","description":"","tags":null,"title":"理解しよう！'delta'","uri":"/godot_recipes/4.x/ja/basics/understanding_delta/index.html"},{"content":" 3D ゲーム開発における3D技術に関するヒント、テクニック、およびチュートリアル。\n目次 ： 3Dアセットの扱い方 FPSキャラクター カメラジンバル 補間カメラ レイキャストを使用した射撃 CharacterBody3Dの移動 3D空間に浮かぶHPバー 投射物の発射 転がるキューブ 移動をカメラに合わせる アーケード風宇宙船 3Dにおけるベクトル描画 アーケードスタイルのカーゲーム アーケードスタイルの飛行機 クリックして移動 滑らかな回転 CharacterBody3D: 表面に位置合わせ 3Dで自動車を作ろう ","description":"","tags":null,"title":"3D","uri":"/godot_recipes/4.x/ja/3d/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 キネマティックカー は斜面を登れるようになりましたが、見た目が少し不自然です。\n解決策 運動体は衝突時に自動で回転しません。画像のように車輪が地面に接触していない場合、手動で車を整列させる必要があります。\nまず、車輪が地面に接触していない状況を検出する必要があります。車に2つの RayCast ノードを追加し、以下のように前輪と後輪にそれぞれ配置します。\n両方の場合、［キャスト先］を（0、-0.25、0）に設定し、「有効」ボックスのチェックを忘れずに行ってください。\n3Dオブジェクトの整列方法 [CharacterBody3D: 表面に合わせる]レシピのコードを再利用します。これをcar_base.gdに追加してください：\nfunc align_with_y(xform, new_y): xform.basis.y = new_y xform.basis.x = -xform.basis.z.cross(new_y) xform.basis = xform.basis.orthonormalized() return xform _physics_process() 関数内で move_and_slide_with_snap() を呼び出した直後に、車両を整列させる必要があるかどうかをチェックします。\n# If either wheel is in the air, align to slope. if $FrontRay.is_colliding() or $RearRay.is_colliding(): # If one wheel is in air, move it down var nf = $FrontRay.get_collision_normal() if $FrontRay.is_colliding() else Vector3.UP var nr = $RearRay.get_collision_normal() if $RearRay.is_colliding() else Vector3.UP var n = ((nr + nf) / 2.0).normalized() var xform = align_with_y(global_transform, n) global_transform = global_transform.interpolate_with(xform, 0.1) 使用方法 どちらの車輪も地面に接していない場合、車はまったく回転しません。\nそれ以外の場合は、前面および背面レイの結果を平均化して使用します。衝突が発生している場合、衝突オブジェクトの表面法線が考慮されます。この方法により、例えばカーブした坂道のように2つの車輪が異なる斜面に接している状況でも、両方の車輪を可能な限り表面に接触させようとする処理が行われます。\nこの画像では、車がどちらの面にも平行ではなく、中間位置に配置されていることがわかります。\nレイが何も検出しない場合は、水平な面と仮定します。これにより、一方のホイールが接触している場合にもう一方の車輪が下がります。\n関連レシピ 運動力学カー：ベースモデル CharacterBody3D：地面に沿う この動画が気に入ったら？ ","description":"","tags":null,"title":"3Dで自動車を作ろう：傾斜面＆スロープ","uri":"/godot_recipes/4.x/ja/3d/kinematic_car/car_slopes/index.html"},{"content":"課題 2Dキャラクターが必要です。アニメーションを含む8方向移動が可能なものが求められます。\n解決策 この例では、Isometric: Mini-Crusaderを使用します。これには待機、歩行、攻撃など8方向に対応したアニメーションが含まれています。\nアニメーションはフォルダ単位で整理されており、各フレームごとに個別の画像が用意されています。ここではAnimatedSprite2Dを使用し、各アニメーションにはその動作方向に基づいて名前を付けます。例えば、右向きで時計回りに移動するidle0から、同じく時計回りに動くidle7までといった具合です。\nキャラクターが移動する際には、動きの方向に基づいて適切なアニメーションを選択します。\nマウスを使って移動します。キャラクターは常にマウスの方向を向き、マウスボタンをクリックするとその方向に走り出します。\nどのアニメーションを再生するか選択するためには、マウスの方向を取得し、それを同じ範囲（0-7）にマッピングする必要があります。get_local_mouse_position()を使用することで、キャラクターに対するマウスカーソルの位置を取得できます。次にsnappedf()関数を使用して、マウスベクトルの角度を最も近い45度間隔（PI/4ラジアン）にスナップさせると、以下の結果が得られます。\n各値を 45°（PI/4 ラジアン）で除算すると、以下のようになります。\n最終的には、wrapi()関数を使用して結果の範囲を0-7にマッピングする必要があります。これにより、正しい値が得られます。この値をアニメーション名の末尾に追加します（“idle”、“run\"など）。こうすることで、正しく動作するアニメーションが完成します。\nfunc _physics_process(delta): current_animation = \"idle\" var mouse = get_local_mouse_position() angle = snappedf(mouse.angle(), PI/4) / (PI/4) angle = wrapi(int(angle), 0, 8) if Input.is_action_pressed(\"left_mouse\") and mouse.length() \u003e 10: current_animation = \"run\" velocity = mouse.normalized() * speed move_and_slide() $AnimatedSprite2D.animation = current_animation + str(a) 動作テストを行ったところ、以下の現象が確認されました：\nキーボード入力 マウスの代わりにキーボード操作を使用している場合、押されているキーに基づいて移動角度を取得できます。それ以外の手順は同様の方法で進行します。\nfunc _process(delta): current_animation = \"idle\" var input_dir = Input.get_vector(\"left\", \"right\", \"up\", \"down\") if input_dir.length() != 0: angle = input_dir.angle() / (PI/4) angle = wrapi(int(a), 0, 8) current_animation = \"run\" velocity = input_dir * speed move_and_slide() $AnimatedSprite2D.play(current_animation + str(angle)) プロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。https://github.com/godotrecipes/8_direction_animation\n","description":"","tags":null,"title":"8方向移動・アニメーション","uri":"/godot_recipes/4.x/ja/2d/8_direction/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 プレイヤー操作可能な3Dキャラクターボディが必要となります。\n解決策 このレシピでは、こちらの可愛らしいタンクモデルを使用します。\nこのモデルはItch.ioで入手できます。他のお好きなモデルを使用しても構いません。ここでは戦車固有の機能は特に実装しません。\nこのアセットの場合、ダウンロードには OBJファイルが含まれており、シーンとしてインポートした方が作業が効率的になります。\nモデルをシーンに追加することはできますが、以下の追加ノードが必要となります。\n衝突判定形状については、戦車の履帯と位置・サイズを合わせた BoxShape を使用します。CamPos は、後続カメラを配置するための Marker3D です。戦車の後方かつ上部、やや下向きに配置されます。\nまた、個別の MeshInstance ノードを Y 軸を中心に 180 度回転させました。これは元々 +Z 方向を向いてモデル化されていたためですが、Godot では -Z が前方方向となるため、戦車が逆向きに見えるようにはしたくないからです。\nスクリプトを追加する前に、「プロジェクト設定」を開き、「インプットマップ」タブで以下の入力を追加してください。\n入力操作 キー 前進 W 後退 S 右移動 D 左移動 A それでは、必要な変数から始めつつ、スクリプトを追加してください。\nextends CharacterBody3D @export var speed = 4.0 @export var turn_speed = 0.8 speed は戦車の移動速度（前進/後退）を、rot_speed は旋回速度をそれぞれ定義します。\nヒント @export でプロパティを宣言しておけば、インスペクタで簡単に調整できるようになります。\nmove_and_slide()メソッドを使用することで、移動コードが非常に簡潔になります。\nfunc _physics_process(delta): velocity.y -= gravity * delta get_input(delta) move_and_slide() このコードでは、重力による下向き加速度を現在の速度に加算し、ユーザー入力を取得し（詳細は後述）、move_and_slide() 関数を呼び出しています。\n次に必要なのは get_input() 関数を定義することです。ここでは入力操作を処理して適用します。\nfunc get_input(delta): var vy = velocity.y velocity = Vector3.ZERO var move = Input.get_axis(\"back\", \"forward\") var turn = Input.get_axis(\"right\", \"left\") velocity += -transform.basis.z * move * speed rotate_y(turn_speed * turn * delta) velocity.y = vy これをもう少し詳しく見ていきます。プレイヤー入力は水平方向の移動に影響を与えるべきです。地面に沿った前後移動と、戦車の中心周りの回転です。Y軸方向の移動は、重力の影響を受けるべきものであり、つまり毎フレーム0に設定すべきではありません。これが、新しい速度ベクトルを水平移動用に割り当てる間、その値を一時的に保持するためにvy変数を使用している理由です。そして最後に、この値を新たに追加してください。\n前方/後方移動にはキャラクターの transform.basis.z を使用しています。 これにより、身体の「ローカル」前方方向へ正しく移動します。\n「これが動作中の戦車です。テスト用シーンとして、地面には StaticBody3D、カメラには 補間付きカメラ レシピを使用した Camera3D を配置しています。\nまとめ これはあらゆる種類の運動学的キャラクターの動作基盤です。ここからジャンプ、射撃、AI挙動などを追加できます。このレシピを拡張した具体例については、関連するレシピを参照してください。\nプロジェクトのダウンロード プロジェクトのサンプルコードをダウンロードする：https://github.com/godotrecipes/characterbody3d_examples\n関連レシピ 3D入門 入力アクション ","description":"","tags":null,"title":"CharacterBody3Dの移動","uri":"/godot_recipes/4.x/ja/3d/characterbody3d_examples/index.html"},{"content":"課題 マウスでリジッドボディを選択して移動させたい。\n解決策 リジッドボディを扱う際には注意が必要です。Godotの物理演算エンジンがこれらの動きを制御しており、これに干渉すると予期しない結果を招くことがあります。重要なのは、オブジェクトのmodeプロパティを活用する点です。これは2Dでも3Dでも同様に適用されます。\nボディ設定 はじめに、リジッドボディオブジェクトを作成します。まずSprite2DとCollisionShape2Dを追加してください。さらに物理特性を設定したい場合はPhysicsMaterialも追加できます。このマテリアルでは Bounce（反発係数） と Friction（摩擦係数） のプロパティを調整できます。\n物理演算エンジンの制御から一時的に解放するために、リジッドボディの freeze プロパティを使用します。ドラッグ操作中も移動可能にしておく必要があるため、デフォルト値である「静的モード」ではなく、フリーズモードを「運動学的」に設定します。\n体を「pickable」というグループに配置します。この設定により、メインシーンで複数のpick可能オブジェクトインスタンスを使用可能になります。Bodyにスクリプトをアタッチし、その_input_eventシグナルに接続してください。\nextends RigidBody2D signal clicked var held = false func _on_input_event(viewport, event, shape_idx): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: print(\"clicked\") clicked.emit(self) マウスクリックを検知すると、ボディへの参照を含むシグナルを発報します。複数のボディが存在する可能性があるため、メインシーン側で各ボディがドラッグ可能かどうか、あるいは既に held(ホールド) 状態にあるかどうかを管理する仕組みを設けます。\nボディがドラッグされている間は、その位置をマウスカーソルに合わせて更新します。\nfunc _physics_process(delta): if held: global_transform.origin = get_global_mouse_position() 最後に、これらの関数はボディがピックアップされた時とドロップされた時に呼び出すものです。freezeをtrueに変更すると物理演算エンジンの処理から除外されます。ただし、他のオブジェクトはこの物体に衝突する可能性がある点に注意してください。もしこれを望まない場合は、ここでcollision_layerおよび/またはcollision_maskも無効化できます。ただその場合、ドロップ時には再度有効化することを忘れないでください。\nfunc pickup(): if held: return freeze = true held = true func drop(impulse=Vector2.ZERO): if held: freeze = false apply_central_impulse(impulse) held = false drop関数内では、freezeを再びfalseに設定した後、ボディは物理エンジンの制御下に戻ります。オプションで衝撃値を指定することで、リリース時にオブジェクトを『投げる』機能を追加することも可能になります。\nメインシーン メインシーンを作成し、静的な障害物を配置するか、TileMapを使用して、選択可能なボディのインスタンスを作成してください。\nメインシーン用のスクリプトです。まず、シーンで使用可能なすべてのボディに対して clicked シグナルを接続するところから始めます。\nextends Node2D var held_object = null func _ready(): for node in get_tree().get_nodes_in_group(\"pickable\"): node.clicked.connect(_on_pickable_clicked) 次に、シグナルを接続する関数について説明します。接続された関数ではheld_object変数を設定して現在ドラッグ中のものを記録し、ボディのpickup()メソッドを呼び出してオブジェクトのピックアップ操作を開始します。\nfunc _on_pickable_clicked(object): if !held_object: object.pickup() held_object = object 最後に、ドラッグ中にマウスボタンを離す場合、その逆の操作を実行できます。\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if held_object and !event.pressed: held_object.drop(Input.get_last_mouse_velocity()) held_object = null 物体に衝撃力を適用するために get_last_mouse_velocity() を使用している点には注意が必要です! 特にmess(質量)が小さい場合、リジッドボディは高速で発射される可能性があります。適切なスケールに調整し、最大値にclamp()をかけることをオススメします。最適な設定を見つけるには実験が必要です。\nプロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。 https://github.com/godotrecipes/rigidbody_drag_drop\n関連レシピ ","description":"","tags":null,"title":"RigidBody2D: ドラッグ＆ドロップ操作","uri":"/godot_recipes/4.x/ja/physics/rigidbody_drag_drop/index.html"},{"content":"課題 多くの2Dゲームでは「3/4視点」と呼ばれるアングルが採用されており、カメラが少し斜め上から見下ろしているような構図になっています。これを実現するためには、より「奥」にあるオブジェクトを先に描画し、手前のオブジェクトの後ろに配置が必要です。実際には、これは「y軸ソート」（描画順序をオブジェクトのy座標に紐付けること）を意味します。画面上の高い位置にあるものほど遠くに位置するため、レンダリング順序もそれに応じて低く設定されることになります。\n以下のような問題が発生します。\n以下のオブジェクトはデフォルトのレンダリング順序（ツリー順）で描画されています。シーンツリー上での配置は以下の通りです。\n解決策 Godotにはレンダリング順序を変更する組み込みオプションが用意されています。任意のCanvasItemノード（Node2DまたはControl）に対して、Y軸ソート有効化 プロパティを有効にできます。この機能が有効になると、すべての子ノードがY軸に沿って並べ替えられます。\n上記の例では、TileMapノード上でプロパティを有効にできます。ただし、まだ解決すべき問題が残っています。\nドロー順序は各オブジェクトの y 座標に基づいています。デフォルトではこれはオブジェクトの中心座標となります。\nオブジェクトが「地面」の上に配置されているように見せたい場合、オブジェクトの position プロパティがスプライトの最下部位置と一致するようにオフセットすることで解決できます。\nだいぶ良くなりました。\nプロジェクトのダウンロード プロジェクトのサンプルコード: https://github.com/godotrecipes/using_ysort\n","description":"","tags":null,"title":"Yソートを使う","uri":"/godot_recipes/4.x/ja/2d/using_ysort/index.html"},{"content":"プレイヤーが自由に画面内を移動できるようになった今、その次のステップとして「射撃」システムを実装していきます。\n再利用可能なオブジェクト プレイヤーは試合中に多数の「弾丸」を発射しますが、すべて同一仕様となります。各弾丸は以下の要件を満たす必要があります。\nプレイヤーのすぐ前方に出現 画面外に到達するまで前進移動 敵キャラとの衝突を検知 すべての弾丸が同じ動作をするように設計されているため、「プロトタイプ」となる基本型を1つ作成すれば、そこから必要な数だけコピーを生成できます。Godotのシーンシステムはこの用途に最適です。\n弾丸シーン メニューからシーン→新規シーンを選択するか、ビューポート上部のタブにある**＋**アイコンをクリックすることで、新しいシーンを作成できます。\nPlayerシーンと同様に、弾丸を正しく機能させるために必要なノードを検討が必要です。ここでも衝突判定用の Area2D コンポーネントを使用することで、物体との接触を検出できます。このため、以下のものが必要になります。\n衝突形状（コリジョンシェイプ）：弾丸が物理的に存在していることをシミュレートするため スプライト：弾丸のビジュアル表示用 画面外検知メカニズム：弾丸が画面から外れたタイミングで自動的に除去する仕組み 以下にノードの設定を示します。\nArea2D - このBulletに名前を付ける Sprite2D CollisionShape2D VisibleOnScreenNotifier2D アセットパックフォルダから、Player_charged_beam (16 x 16).png画像ファイルを、Sprite2Dコンポーネントのテクスチャプロパティにドラッグ＆ドロップしてください。\n船舶画像と同様に、ここにも複数のバージョンが存在するため、Hframesを2に設定し、一度に表示する画像を1つだけにしてください。\nCollisionShape2D の形状を、先ほどPlayerシーンで設定した方法と同じように設定してください。\n弾丸スクリプト ノードBulletにスクリプトをアタッチし、移動の基本設定から始めてください。\nextends Area2D @export var speed = -250 func start(pos): position = pos func _process(delta): position.y += speed * delta このスクリプトはプレイヤー用のものと類似しているので、見覚えがあると思います。変更しているのはposition.yだけです。弾丸を垂直に真上へ発射させるためです。\n定義したstart()関数に注目してください。これにより、プレイヤーが移動して異なる位置から弾丸を発射するため、弾丸の初期座標を設定することが可能になります。\nシグナルの接続方法 次に、bulletノードを選択してから、インスペクターの横にあるノードタブをクリックしてください。\nこれはこのノードが送信可能なすべてのシグナル一覧です。シグナルはGodotで何かが発生したことを通知する仕組みです。この場合、area_entered シグナルを使用することで、この弾丸が他の Area2D ノードに接触するたびに検知できます。\narea_entered シグナルを選択し、接続… ボタン（またはシグナル名をダブルクリック）をクリックしてください。表示されるダイアログでは、何も変更する必要はありません。ただ単に接続 をクリックするだけで完了です。\nbullet.gdを見てください。新しい関数が追加され、名前の横に緑の「接続中」アイコンが表示されます。これはシグナルがこの関数に接続されていることを示すものです。この関数はエリアが何かに接触するたびに呼び出されるため、ここにコードを追加してください。\nfunc _on_area_entered(area): if area.is_in_group(\"enemies\"): area.explode() queue_free() ここで弾が敵に当たったかどうかを確認します（詳細は後述）。当たった場合は、敵に対して爆発処理を実行した後、弾オブジェクトを削除します。\n以下の手順に従って、VisibleOnScreenNotifier2D の screen_exited シグナルも接続してください。\nfunc _on_visible_on_screen_notifier_2d_screen_exited(): queue_free() 次のステップ これで弾丸シーンは完了です。それでは、プレイヤーに射撃機能を追加していきます。\n戻る 次へ ","description":"","tags":null,"title":"シーンの箇条書き表示","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_04/index.html"},{"content":"課題 ゲームセッション間でローカルデータを保存と読み込みたいです。\n解決策 Godotのファイル入出力（IO）システムは FileAccess オブジェクトを基盤として構築されています。ファイルをオープンするには open() メソッドを呼び出します。\nvar file = FileAccess.open(\"user://myfile.name\", File.READ) 警告 ユーザーデータは user:// パスにのみ保存します。エディタから直接実行している場合には res:// も使用できますが、プロジェクトをエクスポートすると、res:// パスは読み取り専用になります。\nファイルパスに続く2番目の引数は「モードフラグ」であり、以下のいずれかを指定できます。\nFileAccess.READ - 読み取り専用でファイルを開く FileAccess.WRITE - 書き込み専用でファイルを開く（存在しない場合は新規作成、存在する場合も先頭から再書き込み） FileAccess.READ_WRITE - 読み書き両用モードでファイルを開く（既存ファイルは切り捨てない） FileAccess.WRITE_READ - 読み書き両用モードでファイルを開く（存在しない場合は新規作成、存在する場合も先頭から再書き込み） データの保存方法 データの保存には、その専用データ型（store_float()、store_string()など）を使用する方法と、汎用的なstore_var()メソッドを使用する方法があります。後者はGodotの組み込みのシリアライゼーション機能を利用してデータをエンコードするため、オブジェクトなどの複雑なデータ構造も扱えます（詳細は後述します）。\nまずは簡単な例から始めてください。プレイヤーのハイスコアを保存する場合を考えます。必要に応じて呼び出せる関数を作成できます。\nvar save_path = \"user://score.save\" func save_score(): var file = FileAccess.open(save_path, FileAccess.WRITE) file.store_var(highscore) スコアを保存していますが、ゲーム開始時にこのデータを読み込み可能な状態にしておく必要があります。\nfunc load_score(): if FileAccess.file_exists(save_path): print(\"file found\") var file = FileAccess.open(save_path, FileAccess.READ) highscore = file.get_var() else: print(\"file not found\") highscore = 0 ファイルを読み込もうとする前に、まずその存在を確認してください。存在しない場合もあるためです。存在しない場合は、デフォルト値を使用できます。\n必要に応じて任意の数の値に対して何度でも store_var() および get_var() を使用できます。\nリソースの保存について 上記の手法は、保存するデータが少数の値で済む場合には非常に有効です。より複雑なケースでは、Godotと同様にリソース形式でデータを保存できます。Godotは全てのデータを.tresファイル（アニメーション、タイルセット、シェーダーなど）として管理していますが、同様の方法でデータを保存できます！\nリソースの保存と読み込みには、Godot クラスの ResourceSaver と ResourceLoader を使いましょう。\n例を挙げると、キャラクターのステータス情報がすべて以下のようにリソースに格納されているとします。\nextends Resource class_name PlayerData var level = 1 var experience = 100 var strength = 5 var intelligence = 3 var charisma = 2 以下のように保存・読み込みができます。\nfunc load_character_data(): if ResourceLoader.exists(save_path): return load(save_path) return null func save_character_data(data): ResourceSaver.save(data, save_path) リソースにはサブリソースを含められるため、プレイヤーのインベントリ用リソースも必要に応じて一緒に読み込むことができます。このように拡張できます。\nJSONについてはどうでしょうか？ 非常によくある質問です（すでに読者の方からも尋ねられているかもしれません）。「JSONを使ってデータを保存したい場合はどうすればいいですか？」というご質問にお答えします。\nセーブファイルにJSONを使用するのは止めてください！\nGodotにはJSONサポートが組み込まれていますが、ゲームデータの保存用途としては設計されていません。JSONは「データ交換」形式であり、異なるデータフォーマットやプログラミング言語を使用するシステム間でデータを相互にやり取りできるようにすることを目的としています。\nJSONにはゲームデータ保存時に不利となる制約があります。JSONは多くのデータ型をサポートしていないため（整数と浮動小数点数の区別ができないなど）、保存や読み込みの際に変換や検証の手間がかかってしまいます。\n時間を無駄にしないでください。Godotの組み込みシリアル化機能を使用すれば、ノードやリソース、さらにはシーンそのものといったネイティブなGodotオブジェクトを一切手間をかけずに保存できます。これにより、コード量が減り、エラーも減少します。\nGodot 自体がシーンやリソースの保存にJSONを使用していないのには理由があります。\nまとめ この記事で「FileAccess」の基本的な機能をざっとご紹介しました。利用可能な「FileAccess」メソッドの一覧については、公式ドキュメントのFileAccessページをご覧ください。\n","description":"","tags":null,"title":"データを保存・読み込む","uri":"/godot_recipes/4.x/ja/basics/file_io/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 TileMapを使用し、自動タイル機能を活用してレベル作成を効率化したいです。\n解決策 本デモでは、以下のタイルセットを使用します。\nメモ これらのタイルはKenney氏の『Topdown Shooter』アートパックに含まれており、こちらから入手できます。https://kenney.nl/assets/top-down-shooter\nこれらのタイルから地図を作成する場合、1枚ずつ手動で配置していくと非常に手間がかかります。角や交差点、終点部分を合わせるために、常に異なるタイルを切り替えながら作業することになるからです。\n自動タイル機能を使用すると、壁を任意の方向に自由に描画でき、アルゴリズムが正しいタイルを選択して全体をシームレスに配置します。\n以下に具体例を示します。\n自動タイリングの仕組みについて 使用しているタイルは 3×3（最小） のタイリング用に設計されています。単一のタイルを3×3グリッドに分割した場合を考えてみます。\nタイルの「アクティブ」部分（つまり壁ではない箇所）に目印を付けることができます。\nこれを各タイルごとに実行すれば、コンピュータはどのタイルを隣接させても確実に互換性が確保されるようにできます。\n3×3のグリッド上には、512通りの組み合わせパターンが存在します（2^9）。これらのほとんどは連続した壁を作る上で実用的ではないため、除外して構いません。実際に適切な形で壁面を覆うためには、48枚のタイルが必要であることが分かりました。これはタイルセットに用意されているものです。以下の7枚のタイルは無視することにします - 右下隅に位置する白い背景のタイルです。\nタイルセットの作成方法 TileMapのプロパティにある「タイルセット」設定で「新規タイルセット」を選択し、クリックするとエディターパネルが開きます。\nテクスチャを追加するには ⊞ ボタンをクリックし、上記で選択したタイルセットを選択してください。次に「新規オートタイル」を選択し、画像全体をドラッグして選択します。スナップ機能を有効にし、設定を調整する必要があるかもしれません。このタイルセットのタイルは 64x64 ピクセルで、各タイル間は 10 ピクセル間隔になっています。独自のアートワークを使用する場合は、必要な値を手動で入力してください。\nオートタイルグループを構成するタイルセットを選択したので、次はビットマスクを設定する段階です。画面上部の「ビットマスク」ボタンをクリックし、各タイル上でクリックを開始してください。マスキングされた部分は赤く表示されます。作業が完了すると、以下のような状態になるはずです。\n※右下のタイルは使用していないことに注意してください。\n最後に、「アイコン」ボタンをクリックして、自動タイルグループのアイコンとして使用するタイルを選択してください。これが描画中にカーソル下に表示されるタイルとなります。\nタイルマップに切り替えてください。正常に描画できるはずです。問題がある場合は、ビットマスクを確認し、すべてのタイルに空白部分が残っていないか確認してください。上記の画像と照合して確認してください。\n完全タイルセット また、自動タイルセットに衝突判定、移動経路、または遮蔽効果を追加することもできます。以下のサンプルプロジェクトをダウンロードすると、すべてのタイル上にポリゴンが定義された完全なタイルセットを入手できます。\n","description":"","tags":null,"title":"タイルマップ：自動タイルを使用","uri":"/godot_recipes/4.x/ja/2d/autotile_intro/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 オブジェクトや画面をぼかすシェーダーが欲しい。\n解決策 shader_type canvas_item; uniform float blur_amount : hint_range(0, 5); void fragment() { COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, blur_amount); } 例えば、シーン切り替え効果のために画面全体を徐々にぼかすには。\nぼかし効果もアニメーション化できます。\nextends Node # Add a ColorRect or other Control set to fill the screen # Place it lower in the tree and/or place in CanvasLayer # so it's on top of the rest of the scene. @onready var blur = $Blur var blur_amount = 0 func _process(delta): blur_amount = wrapf(blur_amount + 0.05, 0.0, 5.0) blur.material.set_shader_param(\"blur_amount\", blur_amount) 関連するレシピ シェーダー入門 シェーダーとの連携 ","description":"","tags":null,"title":"ブラーシェーダー","uri":"/godot_recipes/4.x/ja/shaders/blur/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n基本ゲームプレイが完成したので、次はUIの実装に取り掛かります。タイトル画面、設定画面、そしてゲームオーバー時用のメニュー画面が必要になります。\nメニュー画面 3つの画面は共通のレイアウトと一部機能を共有するため、まずは全てがこのシーンを継承できる基本シーンから始めてください。新しいシーンでは、まず CanvasLayer を作成し、名前を BaseScreen とします。作成したこのシーンは「UI」フォルダに保存してください。\nCanvasLayer (“BaseScreen”) MarginContainer VBoxContainer Label HBoxContainer (“Buttons”) Tween MarginContainer「マージンコンテナ」を使用すると、UI要素が画面端に近づきすぎるのを防止できます。4つの Custom Constants プロパティをすべて20に設定してください。\n次に使用するのはメイン要素を整理するためのVBoxContainerです。このコンテナの［カスタム定数／間隔］設定を150に設定してください。\nLabelノードは画面のタイトルを表示します。［テキスト］フィールドに「タイトル」と入力し、円と同じフォントリソースを読み込みます。\n最後に、「ボタン」という名前の HBoxContainer を追加してください。このコンテナには、後でスクリーンに追加するボタンが格納されます。その 間隔 を 75 に設定します。次にノードを複製して、もう1列分のボタンを作成します。\nスクリーンは画面外に表示開始されるので、ルートノードの オフセット値 を (500, 0) に設定してください。その後、シーンにスクリプトを追加してください。\nextends CanvasLayer @onready var tween = $Tween func appear(): tween.interpolate_property(self, \"offset:x\", 500, 0, 0.5, Tween.TRANS_BACK, Tween.EASE_IN_OUT) tween.start() func disappear(): tween.interpolate_property(self, \"offset:x\", 0, 500, 0.4, Tween.TRANS_BACK, Tween.EASE_IN_OUT) tween.start() このスクリプトでは、画面の表示／非表示を切り替えるためのアニメーション設定します。\nこれで3つの継承されたシーンを作成できます。それぞれについて：ルートノードに名前を付け、ラベルテキストを変更し、「Buttons」コンテナにTextureButtonを追加してください。各ボタンのノーマルテクスチャには、アセットフォルダ内の画像を使いましょう。各ボタンにはその機能に応じた名前を付けてください（例：「再生」「設定」など）。そしてこれらをすべてグループ「buttons」に追加してください。\n以下は、指定されたボタン名を使用した3つのシーンの外観例です。\nさらに「スクリーン」という名前のルートNodeを持つシーンを1つ作成し、その中に3つの画面インスタンスを追加してください。以下のスクリプトを追加すると、シーン遷移と状態管理を処理できます。\nextends Node signal start_game var current_screen = null func _ready(): register_buttons() change_screen($TitleScreen) func register_buttons(): var buttons = get_tree().get_nodes_in_group(\"buttons\") for button in buttons: button.connect(\"pressed\", self, \"_on_button_pressed\", [button.name]) func _on_button_pressed(name): match name: \"Home\": change_screen($TitleScreen) \"Play\": change_screen(null) await get_tree().create_timer(0.5).timeout emit_signal(\"start_game\") \"Settings\": change_screen($SettingsScreen) func change_screen(new_screen): if current_screen: current_screen.disappear() await current_screen.tween.tween_completed current_screen = new_screen if new_screen: current_screen.appear() await current_screen.tween.tween_completed func game_over(): change_screen($GameOverScreen) このスクリプトでは、すべてのボタンを接続するために、pressedシグナルを結び付け、ボタンの名前をパラメーターとして渡します。これにより、_on_button_press()メソッドがそれぞれのボタンに適切な動作を決定できるようになります。\nchange_screen()メソッドは、選択した画面への遷移を処理します。これには、画面に何も表示したくない場合のnullオプションも含まれます。\n以下のコマンドを実行して画面遷移をテストしてください。\nこのシーンをメインシーンでインスタンス化し、start_gameシグナルをメインスクリプト内のnew_game()関数に接続してください。また、new_game()を_ready()から削除するのを忘れないでください。ゲームを実行してみると、正しく開始できるはずです。最後に行うべきことは、ゲームオーバー条件を設定することです。\nジャンパー設定で、died というシグナルを追加し、その可視性通知メソッド内でそのシグナルを発火させます。\n以下のコードを new_game() 関数に追加してください。\nplayer.connect(\"died\", self, \"_on_Jumper_died\") その後、この新しい機能を追加してください。これにより、プレイヤーが死亡した時にすべての円が除去されるようになります。\nfunc _on_Jumper_died(): get_tree().call_group(\"circles\", \"implode\") $Screens.game_over() メニュー画面はシンプルで装飾を省いたものになっていますが、機能的には問題ありません。次回は、スコアカウンターやゲーム内表示などのUI制作をさらに進めていきます。\nGitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\n動画で見る ","description":"","tags":null,"title":"メニュー","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_04/index.html"},{"content":"課題 必要になるのが「追跡ミサイル」です。これは移動する標的を自動で追尾する砲弾です。\n解決策 この例では、プロジェクトイルとして Area2D ノードを使用します。エリアは通常、衝突検出が必要な弾丸に適しています。もし跳ね返る/反射するタイプの弾丸も必要であれば、PhysicsBody 型のノードの方が適しているかもしれません。\nミサイルのノード設定と動作原理は一般的な「単純な」弾丸と同様です。多数の異なる種類の弾丸を作成する場合、継承機能を活用してすべてのプロジェクトイルを同一の基本設定に基づいて構築できます。\n使用するノード：\nArea2D: Missile Sprite2D CollisionShape2D Timer: Lifetime テクスチャについては、お好きな画像を自由に使用できます。一例をご紹介します。\nノードの設定を行い、スプライトのテクスチャと衝突形状を構成します。Sprite2Dノードは必ず 90° 回転させ、右向きになるように調整してください。これにより、親オブジェクトの「前方」方向と一致するようになります。\nスクリプトを追加し、Area2Dのbody_enteredシグナルとTimerのtimeoutシグナルを接続してください。\n以下に開始スクリプトを示します。\nextends Area2D @export var speed = 350 var velocity = Vector2.ZERO var acceleration = Vector2.ZERO func start(_transform): global_transform = _transform velocity = transform.x * speed func _physics_process(delta): velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta func _on_Missile_body_entered(body): queue_free() func _on_Lifetime_timeout(): queue_free() この設定により、発射時に直線軌道で移動する「単純な」ロケットが作成されます。この投射物を使用するためには、インスタンス化した後、start() メソッドを呼び出し、目的の位置と方向を設定するために適切な Transform2D を指定が必要です。\n詳細については以下の 関連レシピ セクションをご覧ください。\nターゲット捜索動作を変更するため、acceleration（加速度）を利用します。ただし、ミサイルが「一瞬で方向転換する」のは避けたいので、制御する「ステアリング力」を調整する変数を追加してください。この設定により、異なる挙動に対応した旋回半径を調整できるようになります。また、ミサイルが追跡対象を把握するためのtarget変数も必要です。これもstart()関数内で適切に初期化します。\n@export var steer_force = 50.0 var target = null func start(_transform, _target): target = _target ... ミサイルを目標に向かって移動させるには、方向転換して加速が必要です（加速度とは速度の変化のことです）。ミサイルは本来、まっすぐ目標方向へ進みたいところですが、現在の速度ベクトルは別の方向に向いた状態です。簡単なベクトル計算によって、このずれ量を求めることができます。\n緑の矢印は必要な速度変化（すなわち加速度）を示しています。ただし、瞬時に方向転換すると不自然に見えるため、この「操舵」ベクトルの長さには制限を設ける必要があります。これを実現するための変数がsteer_forceです。\nこれはその加速度を計算する関数です。注：目標が設定されていない場合、操舵は行われないため、ミサイルはそのまま直線軌道を維持します。\nfunc seek(): var steer = Vector2.ZERO if target: var desired = (target.position - position).normalized() * speed steer = (desired - velocity).normalized() * steer_force return steer 最後に、計算されたステアリング力は_physics_process()内で適用が必要です。\nfunc _physics_process(delta): acceleration += seek() velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta 以下はその結果の一例です。粒子エフェクトや爆発効果などの視覚的演出を少し追加しています。\n以下に完全なスクリプトを示します。上記のエフェクトも含まれています。詳細は関連レシピをご覧ください。\nextends Area2D @export var speed = 350 @export var steer_force = 50.0 var velocity = Vector2.ZERO var acceleration = Vector2.ZERO var target = null func start(_transform, _target): global_transform = _transform rotation += rand_range(-0.09, 0.09) velocity = transform.x * speed target = _target func seek(): var steer = Vector2.ZERO if target: var desired = (target.position - position).normalized() * speed steer = (desired - velocity).normalized() * steer_force return steer func _physics_process(delta): acceleration += seek() velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta func _on_Missile_body_entered(body): explode() func _on_Lifetime_timeout(): explode() func explode(): $Particles2D.emitting = false set_physics_process(false) $AnimationPlayer.play(\"explode\") await $AnimationPlayer.animation_finished queue_free() 関連レシピ スプライトシートアニメーション 見下ろし型キャラクター操作 トランスフォーム操作 ","description":"","tags":null,"title":"追跡ミサイル","uri":"/godot_recipes/4.x/ja/ai/homing_missile/index.html"},{"content":" 3D入門講座 Godot での3D開発のをわかりやすく解説する入門ガイド。\n目次 ： 3Dエディター 3Dオブジェクトのインポート 3Dキャラクターを作る エッジ検出 \u0026 マウスキャプチャ CSGを使う 一人称キャラクター ","description":"","tags":null,"title":"3D入門講座","uri":"/godot_recipes/4.x/ja/g101/3d/index.html"},{"content":"このパートでは、オブジェクト間の接触を検出するためにArea3Dノードを活用する方法を学びました。コインや弾丸、スパイクなどのアイテムをキャラクターが操作できるように実装しました。では更に改良点を見ていきましょう：マウスイベントの取得機能の追加、コインアニメーションの実装、そしてキャラクターが落ちないようにエッジ検出を実装します。\nマウスのキャプチャ マウス操作における一つの問題点は、マウスを画面横から横へ移動させる過程で、最終的にゲームウィンドウの枠を越えて画面上部まで移動してしまうことです。この問題を解決するためには、「マウスカーソルをロックする」必要があります。しかし、これを実装すると、ウィンドウのクローズやその他の操作ができなくなり、完全にマウスが使えなくなってしまいます！再びマウスを自由に使えるようにする仕組みが必要になります。\nまず、マウスをキャプチャするには、メインシーンに以下を追加してください。\nfunc _ready(): Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) この設定で最初の部分が完了しました。これでマウスがゲームウィンドウに捕捉されます。次に、「Esc」キーを押したときにマウス操作を再開させる必要があります。これもメインスクリプトに追加してください。\nfunc _input(event): if event.is_action_pressed(\"ui_cancel\"): Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) ゲームを実行して実際に試してみます。\nマウス操作がキャプチャされていない場合はその動きを無視するようにします。キャラクタースクリプト内で、_unhandled_input() 関数内の該当行を変更してください。\nif event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: 現在別の問題が発生しています。「Esc」キーを押した後、キャプチャ状態に戻る手段がありません。以下の方法でこれを解決します。ウィンドウをクリックすると：\nfunc _input(event): if event.is_action_pressed(\"ui_cancel\"): Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) if event.is_action_pressed(\"shoot\"): if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) この実装は問題なく動作しますが、マウスクリックを再取得するためにクリックすると、同時に弾丸も発射されてしまいます。これはマウスクリックでも同様の処理が行われるためです。これを解決するには、入力イベントを「処理済み」としてマークし、Godotが他のノードにこのイベントを渡さないようにすればよいでしょう。\nif event.is_action_pressed(\"shoot\"): if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) get_tree().set_input_as_handled() 現在は、最初のクリックでマウスをキャプチャしても、弾丸が発射されることはありません。\nアニメーション付きコイン 現在の手順では、前回作成したコインアセットをより動的で魅力的なものに仕上げていきます。Coin.tscnファイルを開き、シーンにAnimationPlayerを追加してください。\n「アニメーション」ボタンをクリックして、「新規作成」を選択し、 「bounce」という名前の新しいアニメーションを作成してください。1秒の再生時間で十分ですが、ループ機能を有効にすることを忘れないでください。\nコインの2つのプロパティをアニメーション化します。位置（Y軸方向、上下）と回転です。スクラバーが時刻0にあることを確認し、両方の［変換］と［回転角度］にキーフレームを追加してください。\nスクラバーを0.5秒の位置に移動させ、翻訳ベクトルの Y 成分を0.3に設定し、キーフレームボタンをクリックします。次に、スクラバーを1.0まで完全に動かし、回転を Y 軸方向で 180° の時点でキーフレーム化します。\nアニメーションの仕上がりを確認するには「再生」ボタンを押してください。各個別キーフレームをクリックして、イージング値を調整してみます。\n最後に、「自動再生」ボタンをクリックして、ゲームを実行するとアニメーションが自動的に開始されるようにしてください。\nエッジ検出 最後に、キャラクターが崖から転落して命を落とすのを防げるか試してみよう。\n以下の手順で進めてください。\nまず、プレイヤーノードにRayCastノードを追加してください。これは細い青色の線として表示されます。デフォルトでは、キャスト方向 プロパティは (0, -1, 0) に設定されており、下向きを指しています。これは正しい設定ですが、さらに前方に移動させて、キャラクターの前面に向かって下向きを指すようが必要です。\nさらに、[有効]プロパティも確認してください。これをチェックしないと、レイキャストは機能しません。\nスクリプト側では、前進時にレイキャストが障害物に衝突していないか確認し、安全に移動できるかを判断します。\n現在の状況は以下の通りです。\nif Input.is_action_pressed(\"move_forward\"): velocity += -transform.basis.z * speed レイが衝突している場合、速度のみを加算する方法を試してみます。\nif Input.is_action_pressed(\"move_forward\") and $RayCast.is_colliding(): velocity += -transform.basis.z * speed 試してみてください。端まで近づくと止まりますが、ちょっと待ってください - ジャンプして前進したことはありますか？今や空中にいるときは前方への移動がキャンセルされます！ 一からやり直しです。\n現在、地面にいるかどうかも確認する必要があります。そうすることで、前方に_歩きながら_レイが衝突しなくなると、何も行わないようにします。それ以外の場合は、通常通り移動します。\nif Input.is_action_pressed(\"move_forward\"): if is_on_floor() and !$RayCast.is_colliding(): pass else: velocity += -transform.basis.z * speed 情報 なぜこのように書いたのか、疑問に思われるかもしれません。読み進めてください― そこにはきちんとした理由があります！\nこれは動作しますが、コードの何かがしっくりきません。不要な条件文があります。これを簡略化できるはずです。ほんの少々のブール代数を駆使すればできます。\n条件分岐のロジックは基本的に次のようなものです。\nif A: do_nothing else: do_something これは以下と同等です。\nif not A: do_something したがって実際に行うべきことは、条件式を以下のように変換することです。\nif !(is_on_floor() and !$RayCast.is_colliding()) この式は動作しますが、読みにくいです。簡略化できませんか？「ド・モルガンの法則」というブール代数の手法を使えばできます。ド・モルガンの法則によれば、以下の関係が成り立ちます。\nnot (A and B) = not A or not B このように適用でき、以下の結果が得られます。\nif Input.is_action_pressed(\"move_forward\"): if !is_on_floor() or $RayCast.is_colliding(): velocity += -transform.basis.z * speed 英語では：\n「床が見えない場合、または目の前に床がある場合は前進してください」\nこれは非常に簡素化された実装例です。例えば、画面の端から横方向や後ろ向きに移動することは不可能なままです。ぜひこのアイデアをさらに発展させ、改善を加えてみてください。ゲームによっては、ゆっくり落下する場合は許容されますが、高速で移動している場合は停止させられる仕組みになっているものもあります。\nまとめ このチュートリアルでは、ゲームに小さな改良を加えました。マウス操作の実装は3Dゲーム全般（特に一人称視点など）で非常に有用であり、レイキャスティング手法も多岐にわたる応用ができます。ここでは一例を紹介したに過ぎません。追加したアニメーションは非常にシンプルなものですが、良いスタート地点となります。今後さらに多くの場面で活用していく予定です。\n次のセクションでは：空間領域構成法（CSG）を使用した方法について解説します。\nこのレッスンの動画版はこちらでご覧いただけます。 ","description":"","tags":null,"title":"エッジ検出 \u0026 マウスキャプチャ","uri":"/godot_recipes/4.x/ja/g101/3d/101_3d_05/index.html"},{"content":"課題 グリッドベースの環境があり、ナビゲーションを可能にする経路探索システムを構築したいです。\n解決策 Godot は経路探索のための複数の手法を提供しています。今回のレシピでは A*(エースター) アルゴリズムを取り上げます。\nA*について A*アルゴリズムは、2点間の最短経路を求めるために広く利用されている手法です。グリッドに限らず、あらゆるグラフ構造データに適用できます。\nAStarGrid2D はGodotの汎用クラス AStar2D をグリッド環境用に最適化した専用バージョンです。グリッドベースで設計されているため、個々のセルや接続関係を手動で追加する必要がなく、より高速かつ簡単にセットアップできます。\nグリッドの設定 最も重要な設定決定事項は、セルのサイズとグリッド自体のサイズです。ここでは例として (64, 64) を使用しますが、ウィンドウサイズは画面上に収まるセル数を決定に使います。ただし、セルサイズが異なっても基本的な動作原理は同じです。\nこのコードを Node2Dに追加してください。\nextends Node2D @export var cell_size = Vector2i(64, 64) var astar_grid = AStarGrid2D.new() var grid_size func _ready(): initialize_grid() func initialize_grid(): grid_size = Vector2i(get_viewport_rect().size) / cell_size astar_grid.size = grid_size astar_grid.cell_size = cell_size astar_grid.offset = cell_size / 2 astar_grid.update() このコードでは、画面サイズを cell_size で割ることでグリッド全体の寸法を計算しています。これにより、AStarGrid2D オブジェクトの size プロパティを適切に設定できます。\noffsetプロパティは、2点間のパスを取得する際に重要な役割を果たします。cell_size / 2を使用することで、パスはセルの角ではなく中心から計算されるようになります。\n最後に、AStarGrid2Dのプロパティを設定または変更した後は必ずupdate()メソッドを呼び出す必要があります。\nグリッド線の描画 本デモでは、グリッドの描画をプログラムコードで実装します。実際のゲームアプリケーションでは、通常 TileMap クラスやその他の視覚的表現を用いて世界を表現になります。\n以下は、グリッドを描画するためのコード例です。\nfunc _draw(): draw_grid() func draw_grid(): for x in grid_size.x + 1: draw_line(Vector2(x * cell_size.x, 0), Vector2(x * cell_size.x, grid_size.y * cell_size.y), Color.DARK_GRAY, 2.0) for y in grid_size.y + 1: draw_line(Vector2(0, y * cell_size.y), Vector2(grid_size.x * cell_size.x, y * cell_size.y), Color.DARK_GRAY, 2.0) これによりグリッドが視覚的に明確に表示されます。\n経路の描画方法 パスを見つけるには、開始点と終了点が必要です。スクリプトの上部にこれらの変数を追加してください。\nvar start = Vector2i.ZERO var end = Vector2i(5, 5) そして以下の行を _draw() 関数に追加して表示させます。\ndraw_rect(Rect2(start * cell_size, cell_size), Color.GREEN_YELLOW) draw_rect(Rect2(end * cell_size, cell_size), Color.ORANGE_RED) 2点間の経路はget_point_path()メソッドを使用して取得できますが、これを可視化する必要もあります。ここではLine2Dを使用できるので、シーンに追加してください。\n以下の方法でパスを取得し、得られた点を Line2D に追加する方法をご紹介します。\nfunc update_path(): $Line2D.points = PackedVector2Array(astar_grid.get_point_path(start, end)) 以下が結果です。\n注：2点間に斜線が引かれています。これはデフォルト設定では経路に斜め移動が含まれるためです。この設定はdiagonal_modeを変更することで変更可能です。\nDIAGONAL_MODE_ALWAYS - デフォルト値。対角移動を使用可能。 DIAGOAL_MODE_NEVER - すべての移動は直行移動のみ。 DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE - この設定では対角移動ができますが、斜め配置された障害物の「間」を経路が通過するのを防ぎます。 DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES - この場合、障害物のないオープンエリアでのみ対角移動ができます。障害物付近ではこのモードは適用されません。 プロパティを変更すると結果が大きく変わる可能性があるため、環境に合わせた調整が重要です。initialize_grid() 関数にこれを追加してください。\nastar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER 現在可能な動きは直交移動のみです。\n障害物の追加 グリッドに障害物を追加することもできます。セルを「固体」としてマークすると、そのセルを通過する経路は除外されます。set_point_solid() 関数を使用すると、セルの状態（固体／非固体）を切り替えることができます。\n壁を描画するコードを追加してください（存在する場合）。固体セルを探し出して色付けします。\nfunc fill_walls(): for x in grid_size.x: for y in grid_size.y: if astar_grid.is_point_solid(Vector2i(x, y)): draw_rect(Rect2(x * cell_size.x, y * cell_size.y, cell_size.x, cell_size.y), Color.DARK_GRAY) _draw() 内でこの関数を呼び出してください。\nその後、マウスを使ってセルをクリックし、その状態を切り替えることができます。\nfunc _input(event): if event is InputEventMouseButton: # Add/remove wall if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: var pos = Vector2i(event.position) / cell_size if astar_grid.is_in_boundsv(pos): astar_grid.set_point_solid(pos, not astar_grid.is_point_solid(pos)) update_path() queue_redraw() 注意：まず is_in_boundsv() をチェックしています。これにより、グリッド領域外にマウスをクリックした場合のエラー発生を防ぐことができます。\n現在では、障害物が経路に及ぼす影響を確認できます。\nヒューリスティック選択について 経路に大きく影響する要素は「ヒューリスティック手法」です。これは「最適な推測」を意味し、経路探索の文脈においては：目標地点へ向かう際に、まずどの方向を試すべきかを決定する方法を指します。\n例えば、ユークリッド距離はピタゴラスの定理を用いて経路を推定します。\nマンハッタン距離は南北または東西方向の距離のみを考慮しますが、以下の点に注意が必要です。\nオクトイルヒューリスティックを適用すると、以下のような経路が得られます。\nこのプロパティを使用してヒューリスティックを選択できます。\nastar_grid.default_estimate_heuristic = AStarGrid2D.HEURISTIC_OCTILE どの方法が最も効果的か（最も見栄えの良い経路が得られるか）は、環境の特性によって異なります。広いオープンスペースが中心で、周囲に障害物が点在しているような状況でしょうか？ それとも、複雑に絡み合った通路が入り組んだ迷路のような空間でしょうか？ ぜひプロジェクトの中で試行錯誤してみてください。\n以下のサンプルプロジェクトをダウンロードして、この設定を実際に試してみます。壁を配置するだけでなく、右クリック／中クリックでエンドポイントとスタート地点を移動させることができます。\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/grid_pathfinding\n","description":"","tags":null,"title":"2Dグリッド上での経路探索","uri":"/godot_recipes/4.x/ja/2d/grid_pathfinding/index.html"},{"content":"課題 3Dゲームオブジェクト（敵キャラクター、プレイヤーキャラなど）用に、フローティング表示の「HPバー」をつけたい。\n解決策 この解決策として、既存の TextureProgressBar ノードをベースにした 2D HPバーを再利用します。すでにテクスチャが設定されており、値と色を更新するためのコードも実装済みです。既に同様のシステムをお持ちの場合は、それをそのまま使用していただいて構いません。サンプルではこのシーンを「Healthbar2D」と名付けます。\n必要なアセットがある場合、バーで使用している以下の3つの画像を紹介します。\nメモ 既存のオブジェクトを再利用すれば、大幅に作業時間を節約できます。HPバーやカメラ、その他一般的なコンポーネントが必要なたびにゼロから作り直す必要はありません。\nプロジェクト設定 例として使用する「モブ」の開始点として、CharacterBody3Dノードを設定します。このノードは自動で出現し、直線的に移動するプログラムが組まれています。また、以下のコードでダメージ処理を実装します。\nfunc _on_input_event(_camera, event, _position, _normal, _shape_idx): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: health -= 1 if health \u003c= 0: queue_free() 単位をクリックするたびに1ダメージが与えられます。合計10ダメージを与えると、そのユニットは破壊されます。この状態を2Dバーを使って視覚的に表現する必要があります。\n2Dを3Dに変換 Sprite3Dを使用することで、2D画像を3D空間で表示することが可能です。新しいシーンに追加し、「Healthbar3D」という名前を付けます。まず設定とサイズ調整を行いますので、 Texture プロパティに緑色のバー画像を設定してください。\nSprite3Dは通常の3Dオブジェクトと同様に動作します。カメラを移動させると、視点が変わるためです。ただし、HPバーは常にカメラの方を向くようにして、いつでも確認できるようにしたいです。\nインスペクターで、 Flags セクションの Billboard を「Enabled(有効)」に設定してください。\n続いてカメラを動かして、テクスチャが常にプレイヤー側を向いているか確認してください。\nこのシーンのインスタンスをMobシーンに追加し、バーをモブの体の上に配置してください。\nビューポートテクスチャ Sprite3D ノードが静的なテクスチャを表示するのではなく、TextureProgressBar を表示したいです。これは、テクスチャをエクスポートできる SubViewport ノードを使用することで実現できます。\n以下の手順で操作してください。\nSubViewport を Sprite3D の子要素として追加してください。 インスペクタウィンドウで、Transparent BG 設定を オン に設定してください。 さらに、HPバーテクスチャのサイズに合わせてビューポートのサイズを設定する必要があり、そのサイズは(200, 26)です。\nインスタンス化する際に、HealthBar2D を Viewport の子要素として配置してください。シーン構成は以下のようになるはずです。\nもし SubViewport が Sprite3D の子要素でなかった場合、インスペクター上で直接スプライトのテクスチャとして設定できます。しかしこれは子要素であるため、適切なタイミングで準備が整っていない可能性があります。そのため、以下のように Sprite3D にアタッチされたスクリプト内で設定します。\nextends Sprite3D func _ready(): texture = $SubViewport.get_texture() 作ったものを統合しよう モブの _on_input_event() メソッド内で、HPを減少させた後に以下を追加してください。\n$HealthBar3D.update(health, max_health) 以下の内容を HealthBar3D.gd に追加してください。\nfunc update_health(_value, _max_value): $SubViewport/HealthBar2D.update_health(_value, _max_value) このコードは、2Dバーに既に存在するupdateメソッドを呼び出しています。進捗バーの値を設定するとともに、バーの色を選択しています。\nfunc update_health(_value, _max_value): value = _value if value \u003c _max_value: show() texture_progress = bar_green if value \u003c 0.75 * _max_value: texture_progress = bar_yellow if value \u003c 0.45 * _max_value: texture_progress = bar_red クリックしてモブのHPゲージが変化する様子を確認してください。\nまとめ このテクニックを使えば、Node2Dノードはもちろん、Controlノード全般（例：LabelやVideoStreamPlayerなど）を3D空間に表示できます。さらに、SubViewportを使えば、2Dゲーム全体を3D空間に「投影」することもできます。\nプロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。https://github.com/godotrecipes/3d_object_healthbars\n","description":"","tags":null,"title":"3D空間に浮かぶHPバー","uri":"/godot_recipes/4.x/ja/3d/healthbars/index.html"},{"content":"課題 キャラクターのボディをリジッドボディと相互作用させたい場合に使用します。\n解決策 メモ このレシピは2Dノードと3Dノードの両方に同様に適用できます。\nデフォルトでは、 move_and_slide() または move_and_collide() で移動させた CharacterBody2D は、衝突する任意の RigidBody2D を押しません。リジッドボディは全く反応せず、単なる StaticBody2D と同じように振る舞います。\n場合によってはこれで十分なこともあります。ただし、ボディーを押し出したい場合は、何点か変更が必要です。\nこの例では、プラットフォーマー用キャラクター レシピで解説されている2Dキャラクターを使用します。この例ではキャラクター移動の最も一般的な方法である move_and_slide() を採用しています。もし代わりに move_and_collide() を使用している場合は、以下の実装を適切に変更してください。\nリジッドボディとのインタラクション方法を決定する際には、以下の2つの選択肢があります。\n物理を無視して直接押すこともできます。Godot 3.x に慣れている方なら、これは\"infinite inertia(無限慣性)“オプションと同等の機能です。 キャラクターの想定する「質量」と「速度」に基づいて押力を発揮させることもできます。これにより「リアルな」結果が得られます - 重い物体は少し、軽い物体は大きく押し出されるようになります。 以下の両方のオプションを試してみます。\n無限の慣性力 この設定には長所と短所があります。最大の利点は、追加コードが不要であることです。必要なのはオブジェクトの衝突レイヤー/マスクを正しく設定することだけです。本事例では、以下の3つの物理レイヤを定義しています。\n硬体オブジェクトについては、「アイテム」レイヤー（レイヤー3）に配置し、マスクはデフォルト設定のまま全レイヤーを覆う状態にしています。\n次に、プレイヤーを「プレイヤー」レイヤー（レイヤー2）に配置し、マスクを設定して「アイテム」を無視するように構成しました。\nゲームを実行してみると、ボックスを自由に移動できることがわかります。なお、箱の質量は関係ありません。すべて同じように押されます。\nこのオプションの欠点もここに現れています。箱の物理演算が無視されているため、壁を貫通したり、上に乗ることすらできません。\n一部のゲームではこれで問題ありませんが、オブジェクトのめり込み（クリッピング）を防ぎたい場合は次の方法を試してみてください。\nインパルスの適用方法 衝突する物体に「押し」を与えるには、インパルスを適用が必要です。インパルスとは「瞬間的な衝撃的の力」のことで、野球でバットがボールを打つようなイメージです。これは、物体に対して連続的に力を加えるのとは異なります。\n# This represents the player's inertia. var push_force = 80.0 func _physics_process(delta): # after calling move_and_slide() for i in get_slide_collision_count(): var c = get_slide_collision(i) if c.get_collider() is RigidBody2D: c.get_collider().apply_central_impulse(-c.get_normal() * push_force) 衝突時の法線ベクトルはリジッドボディの外側を指しているため、これを反転させてキャラクターから離れる方向に調整し、push_force 係数を適用します。これで再び押す動作が可能になります。なお、壁越しにリジッドボディを移動させることはできません。\nリジッドボディの質量と関連させて push_force を調整が必要です。力が大きすぎると衝突が発生してしまいますし、小さすぎると全く押し込めなくなります。\n実験を通じて、特定のゲームに最適な設定を見つけます。\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/character_vs_rigid\n関連レシピ プラットフォームキャラクター 動画を観る ","description":"","tags":null,"title":"CharacterとRigidBodyの相互作用","uri":"/godot_recipes/4.x/ja/physics/character_vs_rigid/index.html"},{"content":"これは、4.0への移行時に注意すべき主要な変更点と「落とし穴」をまとめた随時更新リストです。\n新しい名称 Godot 4での最も大きな変化の一つは、ノード名、関数名、プロパティ名などの大量のリネームです。これらの多くは仕様の一貫性や可読性を向上させるために行われています。特に注意すべき主要な変更点をご紹介します。\n2D/3Dノードの命名規則 - Godot 3.x では2Dノードに「2D」サフィックスが付いていた一方、3Dノードには何も付加されていませんでした。この不整合が解消され、現在ではすべてのノードが「2D」または「3D」を明示するようになりました。具体例：RigidBody2D と RigidBody3D。\n3Dカテゴリにおいて、Spatialノードは名称をNode3Dに統一されています\n最も人気のあるノードの1つである KinematicBody が、CharacterBody2D/CharacterBody3D に名称変更されました。このノードに関するAPIの変更点については、以下をご覧ください。\nPackedScene の instance() 関数は instantiate() に改名されました\npositionおよびglobal_positionプロパティは、3D空間において従来のtranslationとglobal_translationに取って代わり、2Dとの一貫性が保たれます。\nシグナルと呼び出し可能オブジェクト 4.0ではシグナルの扱いが大幅に簡素化されています。signalはネイティブ型として扱われるようになったため、文字列を使用する機会が減り、オートコンプリートやエラーチェック機能が利用可能になりました。これは関数にも適用され、従来のような文字列参照ではなく、直接関数を指定できるようになりました。\n以下に、シグナルの定義、接続、および送出の具体例を示します。\nextends Node signal my_signal func _ready(): my_signal.connect(signal_handler) func _input(event): if event.is_action_pressed(\"ui_select\"): my_signal.emit() func signal_handler(): print(\"signal received\") Tweens（中間アニメーション） Godot 3.5 で SceneTreeTween を使い始めた場合、Godot 4.0 の Tween の使用方法は馴染み深いものでしょう。\nTweenはもはやノードとしてありません。代わりに、必要な時に都度1回限りのTweenアニメーションオブジェクトを作成する方式に変更されました。一度慣れてしまえば、従来の方法よりもはるかに強力で使いやすいものとなっています。\nAnimatedSprite[2D|3D] 3.x系バージョンに慣れているユーザーにとって最も大きな変化は、playingプロパティが廃止された点です。現在はAnimationPlayerとの整合性が大幅に改善されており、アニメーションを自動再生するにはSpriteFramesパネルでオートプレイ機能を有効にするだけで済みます。コード側ではplay()メソッドとstop()メソッドを使用して、再生制御する形式に変更されました。\nキャラクターボディ [2D/3D] このノードにおける最大の変更点は move_and_slide() 関数の使用方法です。パラメーターは一切不要となり、すべてが組み込みプロパティとして実装されました。ネイティブの velocity（速度）プロパティも含まれているため、独自に定義する必要はありません。\nこれらのノードの詳細な使用例については、プラットフォームキャラクターおよび／または基本FPSキャラクタを参照してください。\nタイルマップ 4.0版では TileMap ノードが全面的にリニューアルされました。TileSetの作成方法からタイルの描画・操作方法まで、ほぼすべてが完全に刷新されています。\n「TileMap の使い方」ガイドが間もなく公開されます。\n乱数生成 以下はGDScriptの組み込み乱数生成関数に対する変更点です。\nもう randomize() を呼び出す必要はありません。自動で処理されます。再現性のある「乱数」が必要な場合は、seed() で事前に設定した値を指定します。\nrand_range() は、浮動小数点数の場合は randf_range()、整数の場合は randi_range() にそれぞれ置き換えられました。\nレイキャスティング コード内でレイキャストを実行するために、新たなAPIが導入されました。PhysicsDirectSpaceState[2D|3D].intersect_ray()関数には、専用オブジェクトをパラメーターとして指定します。これによりレイの特性を正確に定義できます。例えば3D空間でレイを描画する場合は。\nvar space = get_world_3d().direct_space_state var ray = PhysicsRayQueryParameters3D.create(position, destination) var collision = space.intersect_ray(ray) if collision: print(\"ray collided\") ","description":"","tags":null,"title":"Godot 3.x からの移行","uri":"/godot_recipes/4.x/ja/basics/migrating/index.html"},{"content":" UI ユーザーインターフェースを構築します。\n目次 ： ラベル コンテナ ハートの器 と 3つの方法 クールタイムボタン ダメージのポップアップ表示 ミニマップ／レーダー レベル選択メニュー リングコマンド ","description":"","tags":null,"title":"UI","uri":"/godot_recipes/4.x/ja/ui/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 ゲーム中のマウスカーソルをカスタマイズしたい。\n解決策 マウスカーソルの設定は Input.set_custom_mouse_cursor() メソッドを使用して行います。必要なのは使用するテクスチャだけです。なお、このテクスチャのサイズは最大で 256x256 ピクセル以内にしてください。\n例えば、以下の画像を使用するには。\n以下で照準をマウスカーソルの中央位置に設定します。\nextends Node2D func _ready(): Input.set_custom_mouse_cursor(cursor_image, Input.CURSOR_ARROW, Vector2(64, 64)) 第2パラメーターはどのシステムカーソルを置き換えるかを指定します。このパラメーターの全ての一覧は入力ドキュメントを参照してください。\n","description":"","tags":null,"title":"カーソルのカスタマイズ","uri":"/godot_recipes/4.x/ja/input/custom_mouse_cursor/index.html"},{"content":" アニメーション Godotのアニメーションシステムを使ってみよう。\n目次 ： スプライトシートアニメーション 近接攻撃 アニメーションツリーを使う ","description":"","tags":null,"title":"アニメーション","uri":"/godot_recipes/4.x/ja/animation/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 タイルマップでアニメーションタイルを使用したい。\n解決策 この問題に対処する最も直接的な方法は、AnimatedTextureリソースを使用することです。\nアニメーションテクスチャの作成方法 以下の水タイルを使用して説明します。\nこれらの画像をダウンロードしてください。water.zip\n画像ファイルをプロジェクトフォルダに解凍してください。 インスペクタで「新しいリソースを作成」ボタンをクリックします。\nAnimatedTextureを選択し、［フレーム］プロパティを5に設定します。各フレームごとに、対応する画像を［テクスチャ］プロパティにドラッグしてください。\nアニメーション全体の速度は Fps プロパティで、各フレームごとの遅延時間は Delay Sec で個別に調整できます。\n「保存」ボタンをクリックしてリソースを保存します。water_anim.tresのような名前を付けてください。\nTileMapでのAnimatedTextureの使用について AnimatedTexture が保存されたので、これでTileSetで使用できるようになります。新規または既存のTileMapを開き、その Tile Set プロパティを選択します。新しいテクスチャをTileSetに追加するには、ボタンをクリックしてください。\n追加された新規テクスチャを選択し、「単一タイルとして新規作成」をクリックします。テクスチャを囲むようにボックスを描きます（「スナップ機能を有効にする」を設定すると作業が楽になります）。\nこれでTileMap内のタイルを選択して、他の通常のタイルと同じように描画できるようになります。\n関連するレシピ タイルマップ：オートタイルの活用 ","description":"","tags":null,"title":"アニメーションタイル(タイルマップ)","uri":"/godot_recipes/4.x/ja/2d/tilemap_animation/index.html"},{"content":"課題 アニメーションの数が増えすぎて、シーン間の切り替え管理が困難になってきていませんか？コードが大量の if 文で埋め尽くされ、少し変更を加えるたびに全体が壊れてしまいそうになっていませんか？\n解決策 アニメーション状態機械を作成するには AnimationTree を使いましょう。これにより、アニメーションを整理できるだけでなく、最も重要な点として、それらの間の遷移を制御することが可能になります。\nはじめに 本デモでは、Elthen氏作の素晴らしいスプライト「Adventurer」を使用しています。この素材を含め、他にも数多くの高品質なアート作品はhttps://elthen.itch.io/で入手できます。\nWe’ll also assume you’ve already set up the character’s animations using AnimationPlayer. Using the above spritesheet, we have the following animations: “idle”, “run”, “attack1”, “attack2”, “hurt”, and “die”.\nアニメーションツリー シーンに AnimationTree ノードを追加してください。［ツリールート］プロパティで「新規アニメーションノードステートマシン」を選択してください。\nAnimationTreeで作成されたアニメーションを制御するノードです。既存のアニメーションにアクセスさせるには、Anim Player プロパティ内の「割り当て」ボタンをクリックし、使用するアニメーションノードを選択してください。\n以下は、状態機械を AnimationTree パネルに設定し始める方法です。\n警告に注意してください。インスペクターで 「_アクティブ」プロパティを「オン」に設定します。\n右クリックして「アニメーションを追加」を選択します。「待機」を選択すると、そのアニメーションを表す小さなボックスが表示されます。「再生」ボタンを押すとアニメーションが実行されるはずです。他のアニメーションを追加する場合も同じ手順で行ってください。\n接続を追加できるようになりました。「ノードを接続」ボタンをクリックして、ノード間でドラッグして接続してください。例として、この2つの攻撃アニメーションを使います。\nアニメーションを選択すると、ツリーは現在のノードから目的地まで接続経路に沿って追従します。ただし、上記の設定では「attack2」を再生しても途中で「attack1」は表示されません。これは、接続におけるデフォルトの「スイッチモード」が「即時」に設定されているためです。まず「移動/選択」ボタンをクリックし、次に「attack1」と「attack2」間の接続ポイントをクリックします。インスペクターで「スイッチモード」を「終点時」に変更してください。同様の操作を「attack2」から「idle」に対しても行います。接続アイコンが から へと変化します。\n「idle」が再生されている状態で「attack2」をクリックすると、二つの攻撃が順番に再生されるようになります。\nしかし、現在「attack2」アニメーションが停止しています。接続時には、進行状況/モードプロパティを「自動」に設定してください。これにより、両方のアニメーション再生後にツリーが「待機」状態に戻ります。なお、接続アイコンが緑色に変化することでこの状態が表示されます。\nアニメーションはトリガーされるたびに連続して再生されます。\nコード内での呼び出し状態 以下に、全てのアニメーションに関する完全なツリー構造を示します。\nそれでは、これらのアニメーションをスクリプトで使用するキャラクターを設定していきます。\nextends CharacterBody2D var state_machine var run_speed = 80.0 var attacks = [\"attack1\", \"attack2\"] @onready var state_machine = $AnimationTree[\"parameters/playback\"] state_machine は状態マシンへの参照を保持しており、これは AnimationNodeStateMachinePlayback 型です。特定のアニメーションを呼び出すには travel() メソッドを使用し、これにより指定されたアニメーションへの接続が辿られます。\nfunc hurt(): state_machine.travel(\"hurt\") func die(): state_machine.travel(\"die\") set_physics_process(false) 以下は、プレイヤーがダメージを受けたり死亡した場合に呼び出される関数の例です。 他のアニメーション（移動、攻撃など）については、入力処理や移動制御コードと組み合わせる必要があります。velocity変数によって、「走行」状態を表示するか「待機」状態を表示するかを決定します。\nfunc get_input(): var current = state_machine.get_current_node() velocity = Input.get_vector(\"move_left\", \"move_right\", \"move_up\", \"move_down\") * run_speed if Input.is_action_just_pressed(\"attack\"): state_machine.travel(attacks.pick_random()) return # flip the character sprite left/right if velocity.x != 0: $Sprite2D.scale.x = sign(velocity.x) # choose animation if velocity.length() \u003e 0: state_machine.travel(\"run\") else: state_machine.travel(\"idle\") move_and_slide() 注意：攻撃アニメーションに移動した後にreturn文を使用しています。これは、関数の後半で「走り」アニメーションや「待機」アニメーションに誤って移動しないようするためです。\nAnimationTreeStateMachine を使用して以下の処理を管理できます。\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/ai_behavior_demos\n関連レシピ スプライトシートアニメーション 見下ろし型キャラクター操作 ","description":"","tags":null,"title":"アニメーションツリーを使う","uri":"/godot_recipes/4.x/ja/animation/using_animation_sm/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 ハートの器のようなバー（または他のアイコンを基調としたバー）を表示したい。\n解決策 プレイヤーのHPを表示する一般的な方法として、一連のアイコン（通常はハートマーク）を使用する手法があります。プレイヤーがダメージを受けると、これらが徐々に消失していきます。\n本レシピでは、以下の3つの方法でこの情報を表示する方法をご紹介します。「シンプル」「空き」、そして「部分」です。\nこの画像はプレイヤーが3のHPを持っている時、バーに表示される内容を表しています。\nシンプル：ハートのみを表示。 空き：空き容量があるハートを表示。 部分：部分的に満たされたハートを表示。 バーの設定手順 使用しているハートのアイコン のサイズは53×45ピクセルです。以下から入手できます。\nKenney.nl: Platformer Art Deluxe\n理想的には、ハートバーは全体のHUD/UIに簡単に組み込めるものであるべきです。したがって、これを別個のシーンとして作成するのが合理的です。まずHBoxContainerから始めてください。これにより要素が適切に整列されます。テーマオーバーライド/定数/分離幅を5に設定してください。\nTextureRect 子要素を追加してください。ハートテクスチャを テクスチャ プロパティにドラッグし、[伸縮モード] を「固定」に設定してください。ノード名を「1」に指定したら、“Ctrl+D\"を押して必要な数（この例では5つ）のハート用に同じノードを複製します。最終的なノード構成は以下のようになります。\nスクリプトの追加方法 以下のスクリプトは3種類すべてのバー構成に対応できるよう設計されています。ゲームで実際に使用するモードは1つで済むはずなので、他のモードに関連するコードは削除しても構いません。\nまず、必要なテクスチャを読み込み、3つのバーモードを定義します。\nextends HBoxContainer enum modes {SIMPLE, EMPTY, PARTIAL} var heart_full = preload(\"res://assets/hud_heartFull.png\") var heart_empty = preload(\"res://assets/hud_heartEmpty.png\") var heart_half = preload(\"res://assets/hud_heartHalf.png\") @export var mode : modes func update_health(value): match mode: MODES.simple: update_simple(value) MODES.empty: update_empty(value) MODES.partial: update_partial(value) バーに対して update_health() を呼び出すと、選択されたモードに基づいて渡された値が表示されます。\nメモ ここでは入力値に対する境界チェックは行いません。ゲームでHPを実装する様々な方法があるためです。\nまず、update_simple() メソッドについて説明します。この処理では、ハートコンテナを順にループしながら、各 TextureRect の表示状態を設定します。\nfunc update_simple(value): for i in get_child_count(): get_child(i).visible = value \u003e i update_empty() は非常に似ていますが、アイコンを隠す代わりに、そのテクスチャを空のコンテナ用に変更します。\nfunc update_empty(value): for i in get_child_count(): if value \u003e i: get_child(i).texture = heart_full else: get_child(i).texture = heart_empty 最後に、部分的に充填された容器については、第3のテクスチャと可能な値の数が2倍になります。\nfunc update_partial(value): for i in get_child_count(): if value \u003e i * 2 + 1: get_child(i).texture = heart_full elif value \u003e i * 2: get_child(i).texture = heart_half else: get_child(i).texture = heart_empty 以下に各バーモードの使用例を示します。\nまとめ このハートバーの設定を、独自のHUD(ヘッドアップディスプレイ)作成の基礎として活用してください。この手法をさらに拡張すれば、多種多様な情報を表示できます。\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらでダウンロードできます。 https://github.com/godotrecipes/heart_bars\n関連レシピ UI：コンテナ –\u003e ","description":"","tags":null,"title":"ハートの器 と 3つの方法","uri":"/godot_recipes/4.x/ja/ui/heart_containers_3/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 RPG風のスキルボタンを作成したい。クールタイム効果も組み込みたい。\n解決策 ボタン用のアイコンが必要な場合、Game-icons.netでは多種多様なデザイン性の高いボタン素材を提供しています。このレシピでもその一部を使用しています。\nノード設定 能力ボタン用のシーンには以下のノードが必要となります。\nAbilityButton: TextureButton Sweep: TextureProgress Timer Counter: MarginContainer Value: Label 選択したアイコンを AbilityButton の Textures/Normal プロパティにドロップしてください。\nSweepノードでは、Presetsメニューから 「Full Rect」 を選択します。**Fill Mode(塗りつぶしモード)**を「Counter Clockwise(反時計回り)」に設定してください。\nまた、クールタイム時の「時計ワイプ」でボタンを暗く表示させたいため、[変調] プロパティに透明度のある濃いグレーを設定してください。\nTimerノードの設定は「One Shot」に設定してください。\nCounterはテキストを保持・配置するためのコンテナです。レイアウトを\"Bottom Wide\"に設定し、Theme Overrides/Constants セクションでは両方の Margin Right と Margin Left を 5 に指定してください。\n最後に、Valueラベルで［水平整列］を「右揃え」に設定し、［テキストクリッピング］を「有効」にしてください。テーマオーバーライド／フォントにフォントを追加してください。動作確認のため、テキスト欄には 0.0 のような値を入力してください。アイコンが白黒の場合は、テーマオーバーライド／定数／アウトラインサイズとして 1 を設定すると効果的です。\nスクリプト 以下のスクリプトを AbilityButton に追加してください。\nTimer の timeout シグナルと AbilityButton の pressed シグナルを接続します。 extends TextureButton class_name AbilityButton @onready var time_label = $Counter/Value @export var cooldown = 1.0 func _ready(): time_label.hide() $Sweep.value = 0 $Sweep.texture_progress = texture_normal $Timer.wait_time = cooldown set_process(false) まず、能力のクールタイム時間を格納するcooldown変数をエクスポートします。次に、_ready()メソッド内でこの値を使用してTimerを設定できます。最後に、カウントダウン中のみ表示させたいため、ラベルは非表示にしておきます。\n次に、TextureProgress 表示に割り当てるテクスチャが必要となります。この例では、ボタンのテクスチャをコピーしていますが、お好みで別のテクスチャを使用しても構いません。\n最後に、スイープ値を 0 に設定し、ノードの処理フラグを false にします。アニメーションは _process() 内で処理するため、クールタイムモードではない時に実行される必要がないようにします。\nfunc _process(delta): time_label.text = \"%3.1f\" % $Timer.time_left $Sweep.value = int(($Timer.time_left / cooldown) * 100) _process() 関数内では、タイマーの time_left プロパティを使用してラベルの text プロパティと Sweepノードの value プロパティを設定しています。\nfunc _on_AbilityButton_pressed(): disabled = true set_process(true) $Timer.start() time_label.show() ボタンをクリックするとすべてが始まります。\nfunc _on_Timer_timeout(): print(\"ability ready\") $Sweep.value = 0 disabled = false time_label.hide() set_process(false) タイマーが切れるとすべての設定は初期化されます。複数のボタンを HBoxContainer に配置するだけで、アクションバーが完成します。\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。 https://github.com/godotrecipes/ui_cooldown_button\n関連レシピ UI: ラベル UI: ユニットHPバー –\u003e ","description":"","tags":null,"title":"クールタイムボタン","uri":"/godot_recipes/4.x/ja/ui/cooldown_button/index.html"},{"content":"課題 以下の状況に対応する：ユーザーが作成したかサードパーティからダウンロードした不正なアニメーション付き3DキャラクターをGodotでセットアップしたい。\n解決策 このレシピでは、既にキャラクターモデルとアニメーションをインポート済みであることを前提としています。まだの方は、アセットのインポート方法を参照してください。参考までに、セクション説明欄でリンクされているアートパックを使用しています。\nキャラクターの準備 以下の理由でキャラクターには CharacterBody3D を選択しました。そのため、シーンはこのような外観になるはずです（メッシュリストが非常に長いため、Rigノードは折りたたみ状態にしています）：\n最初に目につくのは、キャラクターが武器と盾でいっぱいになっていることです！ 作者は親切にも、すべての装備品が正しく取り付けられ、適切な向きになるようにしてくれています。リストをスクロールして、表示したくないアイテムを隠すことができます。\nアニメーションツリー(AnimationTree)について 利用可能なアニメーションが多数ある場合、それらをすべてコードで管理するのは急速に複雑化します。プレイヤーが行っている動作に応じて、どのタイミングでどのアニメーションを再生するかを判断するには、どれほど多くのif文が必要になるか考えてみてください。アニメーションがわずかであれば問題ありませんが、数が増えるとすぐに手に負えなくなり、実用的ではなくなってしまいます。\nまた、キャラクターが静止している場合も考慮します。この時は\"Idle\"アニメーションを再生する必要があります。プレイヤーが「前進」ボタンを押すと、キャラクターは移動を開始し、同時に\"Walking\"アニメーションに切り替わるべきです。しかしこの急激な切り替えは不自然に見えるため、できれば2つのアニメーションをよりスムーズに移行させる「ブレンド処理」が望まれます。\nこれらの複雑なアニメーション問題を解決するには、AnimationTreeノードを使用が必要です。このノードはAnimationPlayerを制御するために設計されており、アニメーションの遷移やブレンド方法を管理する機能を備えています。\nシーンに AnimationTree コンポーネントを追加してください。［インスペクター］で［ツリールート］を新規作成された AnimationNodeStateMachine に設定し、［Anim Player］ではキャラクターの AnimationPlayer を選択します。最後に［有効化］チェックボックスをオンにしてください。\nメモ AnimationTree　が有効になっていると、 AnimationTree でアニメーションを選択できなくなることにご注意ください。変更を加えたりアニメーションをテストする必要がある場合は、作業する際にツリーの 有効 プロパティを必ずオフにしてください。\n待機/歩行/走行サイクルについて このモデルには数多くのアニメーションが同封されています。ここでは特に待機→歩行/走行の遷移、ジャンプ、攻撃モーションに焦点を当てます。他のアニメーションも必要に応じて同様の方法で扱えます。\nAnimationPlayerコンポーネントで、“Idle”（待機）、“Running_A”（前進ランニング）、“Walking_Backwards”（後方歩行）、および\"Running_Strafe_Left\"/“Running_Strafe_Right”（横移動左右ランニング）のアニメーションを設定してください。すべてループ再生に設定することを確認してください。「Play」ボタンを押してテストできます。(▶)。いずれかがループになっていない場合は、設定した後でキャラクターを再インポートしてください（アセットのインポート方法参照）。\nAnimationTreeノードを選択すると、ウィンドウ下部にパネルが開きます。\n例として、空きスペースを右クリックし、「アニメーション追加 → 待機」を選択してから、同様に「1H_Melee_Attack_Chop」アニメーションも追加してください。\n「ノードを接続」ボタンを選択し、開始からアイドルまでの接続線を引いてください。即座に「アイドル」アニメーションが再生されるはずです。\nしかし、このままではうまくいきません。2つのアニメーション間で急速に点滅を繰り返すだけで、両方が「即時遷移」に設定されているため、スムーズな移行ができないからです。\n遷移条件を変更するには、アイコンを使用して選択モードに切り替えた後、いずれかの接続をクリックしてください。インスペクターウィンドウで接続プロパティを確認できます。アイドル状態からアタック状態への遷移では、進行/モード を「有効」（“自動” ではなく）に設定したいです。つまり、指示があった場合にのみ実行されるということです。接続ライン上のアイコンの色が変化することに注意してください。\n接続状態が攻撃モードからアイドルモードに切り替わる際は、切替方式を「終了時」に設定し、進行モードを「自動」にしてください。\nこれで、攻撃ノードの ▶ ボタンをクリックすると再生が始まり、完了するとすぐにアイドル状態に戻ります。\nこの設定により、異なるアニメーションの実装方法とそれらの間の遷移方法について理解できたかと思います。ただし、さらに一歩進んだ内容を扱うため、ゴミ箱アイコンを使用して2つのアニメーションを削除し、代わりに「ブレンドスペース」を設定してみます。\nブレンドスペース 以下の手順で設定を進めてください。\n空欄に右クリックして新規 BlendSpace2D を作成します BlendSpaceの名前をクリックし、IWR（idle-walk-run）に改名します Startから遷移を追加し、ブレンドスペースが自動的に再生されるようにします 鉛筆アイコンをクリックするとブレンドスペースを編集できます。\nこの2D空間はキャラクターの水平方向移動ベクトルを表します。静止状態の場合は座標が(0, 0)となるため、まずポイントを作成ボタンをクリックしてから、グリッド中央をクリックしてアニメーション追加 → 待機姿勢を設定してください。\n上部中央に「ランニングA」アニメーションを追加し、下部中央に「ウォーキング・バックワーズ」アニメを、左右両端にストレーフィングアニメーションを配置してください。\n次に、十字ボタンをクリックしてブレンド位置を設定し、グリッド上でドラッグして移動させてください。アニメーションが極値間でスムーズに遷移するのが確認できるはずです。\nブレンドスペースの実験が終わったら、パネル上部のパス項目にある「ルート」をクリックしてツリーの根に戻ります。\n状態機械の設定 アニメーションツリーの中核となるのがIWRループアニメーションです。このアニメーションはキャラクターが最も多くの時間を過ごす場面で再生されます。他のあらゆるアニメーションは、この基本アニメーションから分岐する形で実装されます（先ほど攻撃動作で行ったように）。\n以下の画像では、他の複数のアニメーションで同じ手法を採用しています。遷移プロパティは、前述の例と同様に設定されている点にご注目ください。\nまた、アニメーションの名前をクリックすることで変更することもできます。中には非常に長い名前のものもありますので。\n※アニメーションが異なるのは「ジャンプ」動作のみです。ジャンプアニメーションは以下の3段階で構成されています。「開始」「着地」で、キャラクターがジャンプを始める時と終了する時にそれぞれ再生されます。また、空中にいる間はループする待機アニメーションが再生されます - 例えば長距離落下した場合などに。\n以下の手順で3つのジャンプアニメーションを追加し、適切にリンクしてください。\n段差から転落した場合、即座にIWRからJump_Idleに直接遷移できるようにする必要がありますが、「ジャンプ」ボタンを押した場合はまずJump_Startを経由する必要があります。\nさらに、IWRからJump_Startへの遷移設定は「自動」のまま変更していません。「有効化」に差し替えるのではなく、この遷移に条件としてjumpingを追加しました。\n同様に、「Jump_Idle」から「Jump_Land」への状態遷移には「grounded」（着地状態）という条件があります。\nこれらの条件をコードで設定し、遷移を実行になります。\n最後に、注意深く観察すると、Jump_LandからIWRへのトランジションが滑らかでないことに気づくかもしれません。これは、2つのアニメーションの最終フレームと最初のフレームが完全には一致していないためです。この問題を解決するには、両者間のトランジションを選択し、小さなXfade時間を0.1に設定することで、スムーズに移行させることができます。\nまとめ 3Dキャラクターのアニメーション設定が完了し、使用準備が整いました。AnimationTree を設定したことで、キャラクターのモーションコード内でアニメーションを選択し、スムーズに遷移させることが格段に容易になりました。\nセクションの説明 を参照すると、3D作業のさらなる事例や、ダウンロード可能なGodotプロジェクトなどの例を確認できます。\n関連動画 https://youtu.be/YrNQCB34PAc\n","description":"","tags":null,"title":"キャラクターアニメーション","uri":"/godot_recipes/4.x/ja/3d/assets/character_animation/index.html"},{"content":"課題 プラットフォームゲームのジャンプ操作に違和感があります。プレイヤーはコントロールが取りづらく、場合によっては足場に正しく飛び降りられないことがあります。\n解決策 この問題を解決するには、「コヨーテタイム」と呼ばれるテクニックが有効です。これはプレイヤーにより高い操作感覚と、プラットフォームの端を移動するプロセスにおいて若干の「余裕時間」を提供します。また、プレイヤーがより自然にジャンプ操作を行えるようになります。\n「コヨーテタイム」の仕組みは以下の通りです。\nプレイヤーがプラットフォームの端から離れた場合、数フレームの間は依然として地面にいるかのようにジャンプできます。\nそもそもなぜコヨーテ？ 「コヨーテタイム」という名称は、地面を見下ろすまで落下しないカートゥーンキャラクターのコヨーテに由来しています。\nこの機能を既存のプラットフォームキャラクターに追加してください。設定方法についてはプラットフォームキャラクターを参照してください。\nタイミング処理に関しては、CoyoteTimerという名前のTimerノードを追加し、設定をOne Shotにします。\nコヨーテタイム管理に必要な新しい変数を用意しましょう。\nvar coyote_frames = 6 # How many in-air frames to allow jumping var coyote = false # Track whether we're in coyote time or not var last_floor = false # Last frame's on-floor state フレーム単位で時間を設定しているため、_ready()内でTimerの長さを設定する際にも次のように変換できます。\n$CoyoteTimer.wait_time = coyote_frames / 60.0 各フレームで現在の is_on_floor() 値を保存し、次のフレームで使用するようにします。したがって、move_and_slide() の後に _physics_process() 内に以下を設定します。\nlast_floor = is_on_floor() ジャンプ入力を検知した場合、キャラクターが床にいる状態か、またはコヨーテ時間モードかどうかを確認します。\nif Input.is_action_just_pressed(\"jump\") and (is_on_floor() or coyote): velocity.y = jump_speed jumping = true コヨーテ状態への移行は、プレイヤーがプラットフォームの端から降りた瞬間に開始されます。これは、前回のフレームでは床の上にいたが、現在は床から離れた位置にいるということです。この条件を確認した上で、ちょうど地面から離れた時にタイマーを起動します。\nif !is_on_floor() and last_floor and !jumping: coyote = true $CoyoteTimer.start() CoyoteTimerは以下のタイミングでコヨーテ状態の終了を通知します。\nfunc _on_coyote_timer_timeout(): coyote = false 3Dキャラクターへの実装方法 この手順は3Dキャラクターにも同様の方法で適用できます。\nプロジェクトのダウンロード 動く床 プロジェクト内のキャラクターにはコヨーテタイムが実装されています。\nプロジェクトコードはこちらからダウンロードできます。 https://github.com/godotrecipes/2d_moving_platforms\n関連レシピ プラットフォームキャラクター ","description":"","tags":null,"title":"コヨーテタイム","uri":"/godot_recipes/4.x/ja/2d/coyote_time/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n最終部分では、ゲームの開始と設定するためのメニュー形式のUIを実装しました。また、スコアなどのゲーム内情報を表示するためのUIも必要です。\nHUDシーン HUDとして使用するCanvasLayerをルートとする新しいシーンを追加してください。以下の2つの子要素を持たせます。「ScoreBox」という名前のMarginContainerと、「メッセージ」という名前のLabelです。\nScene treeは以下のようになっている必要があります。\n以下の設定を行ってください。\nScoreBox のレイアウトを「下部ワイド」に設定 すべてのカスタム定数値を20に設定 HBoxContainer 子要素を追加し、その下に2つの Labelノードを配置 2番目のラベルに「スコア」という名前を付け、 Text プロパティに 100 を入力 HBoxContainer の Alignment を「End」に設定 同じDynamicFontリソースを両方のラベルに追加しますが、最初のラベルでは「ユニークにする」を選択し、そのサイズを 32 に設定します。_Text_プロパティには「スコア」と入力してください。その Size Flags/Vertical で「塗りつぶし」を設定します。レイアウトは以下のようになります。\n次に、Messageノードの処理に移ります。まずフォントを読み込み、 Text プロパティに「メッセージ」を設定します。こうすることで表示内容が確認できるようになります。さらに、フォントリソースで「ユニーク化」（Make Unique）を選択してください（この理由は次のセクションで説明します）。 Align と Valign は「中央」に設定し、 Clip Text は「オン」にしてください。レイアウトに関しては「ワイド・センタリング」を選択します。また、_Grow Direction/Vertical_を「両方」に設定します。\nメッセージアニメーション このゲーム中に表示されるメッセージです（レベルアップ時の通知やボーナス表示など）。アニメーション付きで表示させ、フェードアウト効果を加えたいと思います。シーンに AnimationPlayer を追加してください。\n以下の2つのアニメーションを作成します。初期値を設定するものと、メッセージ表示をアニメーションさせるものです。まず最初のアニメーション「init」を追加し、「ページ読み込み時に自動再生」ボタンをクリックします。持続時間は0.1に設定してください。\n時刻 0 に フォント/サイズ (64) のキーフレームを追加し、表示 を「オフ」に設定するキーフレームも追加してください。\n2番目のアニメーション「show_message」を追加してください。長さを 0.75 に、キーフレームの透明度設定を『オン』に設定してください。\n次に、時刻0と200におけるフォントサイズを64からキーフレーム化します。トラックの更新モードを「連続」に設定してください。\nまた、オブジェクトが成長するにつれて徐々に透明度が低下するようにしたいので、キーフレームを使用してアルファ値を255から0まで変化させます。\n以下にアニメーション設定の例を示します。\nアニメーション再生時の挙動：\nHUDスクリプト 次に、ディスプレイを更新するためのメソッドを含むスクリプトをシーンに追加してください。\nextends CanvasLayer func show_message(text): $Message.text = text $AnimationPlayer.play(\"show_message\") func hide_score(): $ScoreBox.hide() func show_score(): $ScoreBox.show() func update_score(value): $ScoreBox/HBoxContainer/Score.text = str(value) メインシーンにHUDインスタンスを作成し、_ready()関数と_on_Jumper_died()関数内に$HUD.hide_score()を追加してください。new_game()関数では、HUDを表示してメッセージを表示します。以下のように実装します。\n$HUD.show_score() $HUD.show_message(\"Go!\") スコアを追加するには、new_game() 関数内で score 変数を作成し、初期値として 0 を設定してください。_on_Jumper_captured() 関数内では、この変数の値を 1 ずつ加算します。それぞれの処理後に $HUD.update_score(score) を呼び出すことを忘れないでください。\n次のパートでは、ゲームにサウンドと色を追加していきます！\nGitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\n動画で見る ","description":"","tags":null,"title":"スコアとHUD","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_05/index.html"},{"content":" サークルジャンプ ゲーム開発を基礎から学べる連載シリーズ。 この連載シリーズでは、ワンタップで遊べるモバイルゲーム『サークルジャンプ』を開発していきます。まずはこのゲームの概要から見てみてください。\n各エピソードでは、新機能の追加やバグ修正を行い、その過程を分かりやすく解説していきます。\nこのゲームは以下のプラットフォームで利用できます。\nAndroid：Google Playストアにて配信中 iOS版は近日公開予定 Webブラウザ版：今月のゲームページで利用可能 以下のサイトからデスクトップ版をダウンロードすることもできます。itch.io：\nkidscancode 作「サークルジャンプ」 ゲームの全ソースコードはGitHubで公開されています。\n","description":"","tags":null,"title":"モバイルゲーム：サークルジャンプ","uri":"/godot_recipes/4.x/ja/games/circle_jump/index.html"},{"content":"Bulletシーンでは、プレイヤーが射撃するたびに再利用可能なオブジェクトを生成できます。\nプレイヤーへの追加方法 以下のコードをPlayerスクリプトに追加してください。\n@export var cooldown = 0.25 @export var bullet_scene : PackedScene var can_shoot = true 以下の2つの@export変数を使用すると、インスペクターで設定可能なため、クールタイムを調整できます。プロパティをクリックしてbullet.tscnファイルを選択する形でbullet_sceneを設定してください。\ncan_shootはプログラミング用語で言うところの「フラグ」です。これはある条件を制御するブール型変数です。この場合、プレイヤーが射撃可能かどうかを決定します。クール期間中、この変数はfalseに設定されます。\n次に、Bulletと同様にstart()関数を追加してください。これにより、プレイヤーの初期値を設定できるほか、ゲームが再起動するたびにこれらの値をリセットできるようになります。\nfunc _ready(): start() func start(): position = Vector2(screensize.x / 2, screensize.y - 64) $GunCooldown.wait_time = cooldown これにより、プレイヤーは画面の中央下部に配置され、最適なスタート位置となります。また、クールタイムタイマーが適切な待機時間を設定することを保証します。\nshoot()関数は、『射撃』入力が押されるたびに呼び出されるようになります。\nfunc shoot(): if not can_shoot: return can_shoot = false $GunCooldown.start() var b = bullet_scene.instantiate() get_tree().root.add_child(b) b.start(position + Vector2(0, -8)) この関数の最初の処理は、プレイヤーが射撃可能かどうかを確認することです。もし許可されていない場合、return文によって即座に関数が終了します。\nプレイヤーが射撃可能状態の場合、フラグをfalseに設定し、クールタイムタイマーを開始します。その後、新しい弾丸オブジェクトを作成しゲームに追加し、そのstart()関数を呼び出して正しい位置に配置します（プレイヤーの宇宙船すぐ上）。\nこの関数を呼び出せるのは、プレイヤーがキーを押している時です。_process() 関数の末尾、position.clamp() 行の後にこれを追加してください。\nif Input.is_action_pressed(\"shoot\"): shoot() さらに、GunCooldown の timeout シグナルも接続する必要があります。\nfunc _on_gun_cooldown_timeout(): can_shoot = true クールタイムが終了したら、再び射撃を許可できます。\nシーンを実行して、射撃アクションを押してみてください。\nツリーへのインスタンス追加について 注意：新規に生成した弾丸はシーンツリーのルートノード（get_tree().root）の下に子要素として追加されたことに留意してください。プレイヤー船の子要素としては追加していません。これは重要な点です。もし弾丸を船の子要素にしていた場合、船が移動するたびにそれらが「固定」されてしまうからです。\n次のステップ シューティングゲームは撃つ対象がなければ面白くありません。敵キャラの作成に取り掛かりますが、その前にプレイヤーや敵、その他のゲーム内オブジェクトを配置するシーンを作成する必要があります。\n戻る 次へ ","description":"","tags":null,"title":"射撃","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_05/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 プレイヤー／モブなどから投射物を発射させたい。\n解決策 この例では、CharacterBody3D：移動操作 で設定した「ミニ戦車」を使用します。\n弾丸の設定 まず、インスタンス化可能な「弾丸」オブジェクトを設定します。使用するノードは以下の通りです。\nArea3D: Bullet MeshInstance CollisionShape メッシュには、Godotに標準で備わっているプリミティブ形状を使用するか、以下のようなものを作成できます。\nメモ ここに掲載している弾丸モデルを使用したい場合は、Kenney’s “Blaster Kit”から入手できます。\nメッシュを MeshInstance に追加し、衝突形状もそれに合わせてスケール調整してください。\n警告 必ず MeshInstance を Area3D ノードの前方方向（-Z 軸）と揃えてください。そうしないと、弾丸が正しく飛んでいるように見えませんよ！\nスクリプトを追加し、Area3Dのbody_enteredシグナルを接続してください。\nextends Area3D signal exploded @export var muzzle_velocity = 25 @export var g = Vector3.DOWN * 20 var velocity = Vector3.ZERO func _physics_process(delta): velocity += g * delta look_at(transform.origin + velocity.normalized(), Vector3.UP) transform.origin += velocity * delta func _on_Shell_body_entered(body): emit_signal(\"exploded\", transform.origin) queue_free() カスタムの重力ベクトル g を使用することで、戦車の砲弾が綺麗な弧を描くように、大砲からどのように飛ぶかを制御できます。もし、飛び道具を直線的に移動させたい場合は、_physics_process() で重力を適用している行を削除してください。\nlook_at() をフレームごとに使用すると、弾丸は常に進行方向を向くようになります。\nまた、explodedシグナルも発出します。これを利用して爆発エフェクトやダメージ効果を実装できます（ただし詳細な実装は別のレシピで解説します）。\n射撃 タンク内（または射撃オブジェクト）の任意の位置に、弾丸が出現させたい場所に Marker3D 子要素を追加してください。今回の戦車の場合、砲身の先端部分に配置します。\n次に、インスタンス化する弾シーンを追加する方法です。\n@export var Bullet: PackedScene そして、_process() または _unhandled_input()（入力をキャプチャしている箇所）に、以下のコードを追加して弾丸を生成してください。\nif Input.is_action_just_pressed(\"shoot\"): var b = Bullet.instantiate() owner.add_child(b) b.transform = $Cannon/Muzzle.global_transform b.velocity = -b.transform.basis.z * b.muzzle_velocity これで完了です。シーンを実行して実際に試してみます。\n関連レシピ CharacterBody3D: 移動方法 初めてのGodot: 3D入門 –\u003e\n","description":"","tags":null,"title":"投射物の発射","uri":"/godot_recipes/4.x/ja/3d/3d_shooting/index.html"},{"content":"課題 2Dプラットフォーマーゲームには動く床が必要です。\n解決策 この問題には複数のアプローチ方法があります。ここではプラットフォーム部分に AnimatableBody2D を使用し、Tween で移動させる手法を採用します。これにより、多様な動作パターンを実現しながら、必要なコード量を最小限に抑えることができます。\n情報 この移動プラットフォームの実装方法は、Tweenではなく AnimationPlayer を使用することでもできます。基本的なセットアップ手順は同様ですが、代わりにTweenコードの代わりにボディの position プロパティをアニメーション化します。\nセットアップ方法 まずはプラットフォームキャラクターレシピを使用して、基本的なプラットフォーマーのセットアップから始めてください。このレシピで定義されている基本移動機能は、指定されたプラットフォームと問題なく連携します。もしレシピをカスタマイズしていたり、独自の実装を使用している場合でも、動作原理は同じですのでご安心ください。\nプラットフォームの作成 プラットフォームシーンには以下のノードが含まれています。\nNode2D (“MovingPlatform”)：この Node2D の親ノードは、プラットフォームの「アンカー」または開始点として機能します。アニメーションでは、この親ノードを基準としてプラットフォームの 座標 を制御します。 AnimatableBody2D：これは実際に移動するプラットフォーム本体を表します。このノードが移動対象となります。 Sprite2D：ここではスプライトシート、個別の画像、あるいは ノードとしてタイルマップを使用することもできます。 CollisionShape2D：ヒットボックスの大きさが大きすぎると、プレイヤーがプラットフォームの端から「浮いて」見える原因になるので注意してください。 Sprite2Dのテクスチャと衝突形状を適切に設定してください。AnimatableBody2Dでは、Sync to Physics(物理演算と同期) プロパティを「オン」に設定します。コードでボディを動かしているため、これにより物理演算ステップ時に正しく移動され、プレイヤーや他の物理オブジェクトと連動した動きを維持できます。\n次にルートノードのNode2Dにスクリプトを追加してください。\nextends Node2D @export var offset = Vector2(0, -320) @export var duration = 5.0 func _ready(): start_tween() func start_tween(): var tween = get_tree().create_tween().set_process_mode(Tween.TWEEN_PROCESS_PHYSICS) tween.set_loops().set_parallel(false) tween.tween_property($AnimatableBody2d, \"position\", offset, duration / 2) tween.tween_property($AnimatableBody2d, \"position\", Vector2.ZERO, duration / 2) 以下の機能をスムーズに動作させるために、Twien のオプションを活用しています。\nset_process_mode()：物理演算処理段階でのみ移動が行われるようにします。 set_loops()：Tween再生を繰り返す設定です。 set_parallel(false)：デフォルトでは、すべての tween_property() 変更が同時に実行されます。これを無効にすると、2つの動作が順次行われます。オフセットの一方端に移動した後、開始位置に戻るという流れになります。 翻訳された2つのプロパティを使用することで、プラットフォームの移動を調整できます。offsetを設定してTweenが開始点から相対的に移動する位置を指定し、durationを設定してサイクルを完了するまでの時間を決定します。\n自分のレベル／ワールドにプラットフォームを追加し、実際に試してみてください。\nプロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。 https://github.com/godotrecipes/2d_moving_platforms\n関連レシピ プラットフォームキャラクター 近日公開予定 –\u003e\n","description":"","tags":null,"title":"動く床","uri":"/godot_recipes/4.x/ja/2d/moving_platforms/index.html"},{"content":" 入力 入力処理 - キーボード・マウスからゲームコントローラー、タッチスクリーンまで幅広く対応させます。\n目次 ： マウス入力 入力アクション マウスのキャプチャ 実行中に入力アクションを追加する カーソルのカスタマイズ マウスドラッグでユニットを選択 ","description":"","tags":null,"title":"入力","uri":"/godot_recipes/4.x/ja/input/index.html"},{"content":" ゲーム数学 数学はゲーム開発において非常に重要な役割を果たします。その中には学校で習った内容もあれば、これまで触れたことのない概念もあるかもしれません。ここでは基礎知識を身につけるためのガイドと、これらの数学的概念が実際のゲーム制作にどのように応用されているかの具体例を紹介します。\n目次 ： 補間 トランスフォーム ベクトル：内積と外積の活用 ","description":"","tags":null,"title":"ゲーム数学","uri":"/godot_recipes/4.x/ja/math/index.html"},{"content":" AI／振る舞い 自律的なアクションと、知的なキャラクターの実装を。\n目次 ： プレイヤーを追いかける パスを追従する 追跡ミサイル 状況に基づく操縦 追いかけるペット ","description":"","tags":null,"title":"AI／振る舞い","uri":"/godot_recipes/4.x/ja/ai/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\nこれまで、インポートしたモデルを使って3D環境を設計する方法を見てきました。では、壁やドア、スロープなどの要素を含む部屋のようなものを作りたい場合、すでに適切なモデルがない場合どうすればいいでしょうか？そこで活躍するのがCSGです！\nCSGとは何ですか？ CSGはConstructive Solid Geometry（空間領域構成法）の略です。これにより、基本的な形状を組み合わせて複雑な立体図形を構築できます。形状は、ユニオン（結合）、インターセクション（交差）、サブトラクション（減算） といった論理演算によって組み合わせることができます。\nCSGは環境やゲームオブジェクトのプロトタイピングに最適なツールです。このチュートリアルの後半では、この技術を活用して、スロープや壁、プラットフォームを備えたシンプルなFPS風レベルを作成します。\nその前に、まずCSGノードの動作原理について簡単にご説明します。\nCSGの基本概念 新しいシーンを Node3D ルートで作成し、その後子ノードを追加して検索ボックスに「CSG」と入力してください。\n以下が利用可能なCSG形状です。開始するにはCSGBoxを選択してください。以下のような基本キューブメッシュが表示されます。 プロパティは以下の通りです。\n操作プロパティは、CSG形状の組み合わせ方法を決定するものです。選択可能なオプションは以下の通りです。\nUnion(結合) - 図形が統合され、内側の領域は削除されます。 Intersection(交差) - 重なっている部分のみ保持されます。 Subtraction(差集合) - 2つ目の形状が1つ目の形状から「切り取られ」ます。 CSG操作は子形状を追加することで行われます。CSGBoxにCSGCylinderの子要素を追加してください。上部のサイズハンドル（オレンジ色のドット）をドラッグして少し高さを変更できます。さらに、プロパティ_Sides_を増やすことでより円筒形に近い見た目になります（ここでは値を20に設定しています）：\nデフォルトでは、形状の演算は［結合］に設定されています。円柱形状がキューブに ［追加］されようとしています。これを［交差］に変更してみてください。\n「減算」について説明します。\nこれら3つの操作を通じて、複雑な形状を作成する可能性がすでにお分かりいただけたことでしょう。\nCSGCombiner このノードは「空の」形状です。図形を整理する際に使用します。CSGCombiner の子要素は、上記と同様のルールに従って統合されます。\n部屋の構築 以下の機能を備えた実用的なシーンを作成しましょう：障害物を配置した広い部屋と、キャラクターが操作可能で、今後のチュートリアルで活用できる要素。まず、作業を開始できるルートノードとして Node3D を持つ新しいシーンを作成してください。\nまず CSGBox を作成し、以下のプロパティを設定します。\n幅 (Width) と奥行き (Depth) を 20 に設定 高さ (Height) を 5 に設定 このボックスは内部に侵入するものとして扱う必要があるため、[面反転] (Invert Faces) プロパティも有効にしてください。これにより形状が反転し、内側の壁面が外側になります。さらに、[衝突機能使用] (Use Collision) プロパティもチェックしてください。これにより、物理エンジンはこの形状を静止物体オブジェクトとして適切に処理します。\nプレイヤーキャラクターのインスタンスを追加し、新しい部屋で自由に移動できることを確認します。\nヒント 前のパートでは、マウスカーソルをキャプチャ／リリースするコードを追加しましたね。このシーンにも同じコードをコピーする必要があることを覚えておいてください。\n機能の追加について 以下は作成を開始するのに役立つアイデア例です。\nヒント 各オブジェクトに CSGCombiner を使用すると、それらの整理や複製が格段に容易になります。必ず Use Collision を有効にしてください。\nドア付き壁 以下の手順で進めます。\nCSGBox オブジェクトを追加してください。 その幅を 0.5、高さを元の部屋と同じ 5 に設定します（この高さは元の部屋と一致させます）。 奥行きは部屋の大きさの約半分になるように調整してください。 ここではスナップ機能を有効にしておくと、配置作業をより簡単に行えます。 子ノードとして別のCSGBoxを追加し、それを「Subtract」に設定します。ドアのように見えるようにサイズを調整してください。円柱形状を使用してアーチ状のポータルを作成することもできます。\nスロープ 階段用にはCSGPolygonを使用します。このCSG形状では、指定した多角形を任意の深さまで押し出すことができます。デフォルトでは正方形ですが、頂点の追加・削除もできます。階段の場合、3つの点を指定します。\nCSGPolygonを追加します。ポリゴン プロパティをクリックして点の数を調整できます。その後、3つの点を任意の位置にドラッグして移動できます。すべてを正確に配置するには、インスペクターに座標を直接入力できます。\nこの画像では、スロープを作成し、エッジ部分を形成するためにCSGBox形状を何個か追加しました。\nまとめ 形状には必ず Material を追加し、好みに合う Albedo (アルベド) カラーを選択してください。以下の例では、茶色系の色を選択しています。\n以下に考えられる部屋の配置例を示します。\n再生ボタンを押して、部屋を自由に歩き回れるようになるはずです。\n現在このエリアには特徴がありませんが、見た目をより印象的にするために、各部屋ごとに個別のライトを追加できます（ライティングに関する詳細は後日解説するチュートリアルで詳しく説明します）。以下に、シーンにOmniLightとSpotLightノードを追加する例を示します。\nまとめ CSG は、Blender などの別のモデリングアプリケーションを使用せずに Godot 内で直接オブジェクトを構築できる強力なツールです。ゲーム用テストレベルを作成する場合や、最終的な環境を構築する場合に特に役立ちます。ただし、CSG ツリーが複雑になるほどパフォーマンスに負荷がかかる点に注意が必要です。可能な限り CSGCombiner を使用してシーンを複数の独立した CSG ツリーに分割することで、この影響を最小限に抑えることができます。\n次のセクションでは、3Dゲームで人気のスタイルである「ファーストパーソン」について解説します。\nこのレッスンの動画バージョンはこちらからご覧いただけます。 ","description":"","tags":null,"title":"CSGを使う","uri":"/godot_recipes/4.x/ja/g101/3d/101_3d_06/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n設定シングルトン まず、スクリプトタブで［ファイル］→［新規スクリプト］を選択して新しいスクリプトを追加してください。スクリプト名は settings.gd とします。\nこのスクリプトでは、ゲームの設定パラメーターを配置します。\nvar enable_sound = true var enable_music = true var circles_per_level = 5 スクリプトを 自動読み込み に登録するには、「プロジェクト設定」を開き、「自動読み込み」タブを選択してください。ロードしたいスクリプトのフォルダをクリックし、その後「追加」をクリックします。\nサウンドの追加方法 サウンドを再生するために、複数のAudioStreamPlayerノードを異なるシーンに追加してください。\nまず Main シーンに「Music」という名前を追加してください。Stream プロパティには res://assets/audio/Music_Light-Puzzles.ogg を使いましょう。\nScreens シーンに新たに「Click(クリック)」という画面を追加してください。ボタンをタップした際にこの画面が表示されるようにします。アセットフォルダからmenu_click.wavを使用します。\nCircleシーンに、名前を『Beep』としたオーディオプレイヤーを追加し、89.oggサウンドファイルを使用します。\n最後にジャンパー部では、「ジャンプ」と「キャプチャー」の2種類の効果音が必要です。それぞれ70.oggと88.oggを使いましょう。\nサウンドを再生するには、それぞれの play() メソッドを呼び出すだけです。Main.new_game() にこれを追加してください。\nif settings.enable_music: $Music.play() および Main._on_Jumper_died() 関数：\nif settings.enable_music: $Music.stop() Screens.gd ファイルの _on_button_pressed() 関数に以下を追加してください。\nif settings.enable_sound: $Click.play() 円軌道において、限定された円（制限軌道）が1周完了した際にビープ音を再生したいです。これはcheck_orbits()関数内で実装します。\ncurrent_orbits -= 1 if settings.enable_sound: $Beep.play() そして Jumper.gd に、次のようにサウンドを追加します:\nfunc jump(): target.implode() target = null velocity = transform.x * jump_speed if settings.enable_sound: $Jump.play() func _on_Jumper_area_entered(area): target = area velocity = Vector2.ZERO emit_signal(\"captured\", area) if settings.enable_sound: $Capture.play() ゲームを起動し、すべての音が期待通りに聞こえるか確認してください。\nサウンド設定 音声機能が正常に動作するようになったので、「設定」画面で音と音楽を切り替えられるボタンも接続できるようになりました。\nボタンの表示を、プロパティの現在のオン／オフ状態に合わせる必要があります。まずはテクスチャを読み込んでから、必要に応じて割り当てられるようにします。\nvar sound_buttons = {true: preload(\"res://assets/images/buttons/audioOn.png\"), false: preload(\"res://assets/images/buttons/audioOff.png\")} var music_buttons = {true: preload(\"res://assets/images/buttons/musicOn.png\"), false: preload(\"res://assets/images/buttons/musicOff.png\")} 現時点では、ボタンが押されている状態を適切に処理できていません。問題は、現在ボタンの名前を渡しているため、テクスチャを変更することができない点です。代わりに、register_buttons() 関数を再設計し、ボタン自体への参照を渡すようにします\nbutton.connect(\"pressed\", self, \"_on_button_pressed\", [button]) その後で _on_button_pressed() を以下のように更新できます。\nfunc _on_button_pressed(button): if settings.enable_sound: $Click.play() match button.name: \"Home\": change_screen($TitleScreen) \"Play\": change_screen(null) await get_tree().create_timer(0.5).timeout emit_signal(\"start_game\") \"Settings\": change_screen($SettingsScreen) \"Sound\": settings.enable_sound = !settings.enable_sound button.texture_normal = sound_buttons[settings.enable_sound] \"Music\": settings.enable_music = !settings.enable_music button.texture_normal = music_buttons[settings.enable_music] カラーテーマ設定 さらに、色テーマをカスタマイズできる機能も追加予定です。これらは以下のような方法で変更できます。設定項目として選択するか、プレイヤーがより高いレベルに到達するにつれて段階的に変化させる、といった方法が考えられます。\nカラースキームデータを辞書形式で保存します。キーは各配色の「名称」となります。各配色自体も辞書形式とし、キーはその配色を使用するゲームコンポーネントを示します。\n以下を settings.gd に追加してください。\nvar color_schemes = { \"NEON1\": { 'background': Color8(0, 0, 0), 'player_body': Color8(203, 255, 0), 'player_trail': Color8(204, 0, 255), 'circle_fill': Color8(255, 0, 110), 'circle_static': Color8(0, 255, 102), 'circle_limited': Color8(204, 0, 255) }, \"NEON2\": { 'background': Color8(0, 0, 0), 'player_body': Color8(246, 255, 0), 'player_trail': Color8(255, 255, 255), 'circle_fill': Color8(255, 0, 110), 'circle_static': Color8(151, 255, 48), 'circle_limited': Color8(127, 0, 255) }, \"NEON3\": { 'background': Color8(0, 0, 0), 'player_body': Color8(255, 0, 187), 'player_trail': Color8(255, 148, 0), 'circle_fill': Color8(255, 148, 0), 'circle_static': Color8(170, 255, 0), 'circle_limited': Color8(204, 0, 255) } } var theme = color_schemes[\"NEON1\"] 現在各オブジェクトに対して、設定プロパティに基づいて色を設定が必要です。\n円形の場合、シェーダーマテリアルリソースを使用して色を設定しています。共有リソースを使用しているため、1つの円の色を変更するとすべての円に影響が及びます。これを回避するため、各サークルのマテリアルを個別にしてみます。\n$Sprite.material = $Sprite.material.duplicate() $SpriteEffect.material = $Sprite.material 円形の色はそのモードによって決定されるため、実際の選択処理は set_mode() 関数内で行います。\nfunc set_mode(_mode): mode = _mode var color match mode: MODES.STATIC: $Label.hide() color = settings.theme[\"circle_static\"] MODES.LIMITED: current_orbits = num_orbits $Label.text = str(current_orbits) $Label.show() color = settings.theme[\"circle_limited\"] $Sprite.material.set_shader_param(\"color\", color) 次に_draw()関数において、限定円の描画部分では、赤のカラー値をsettings.theme[\"circle_fill\"]に置き換えてください\nプレイヤーの設定では、_ready() 内で色を指定します。\nfunc _ready(): $Sprite.material.set_shader_param(\"color\", settings.theme[\"player_body\"]) $Trail/Points.default_color = settings.theme[\"player_trail\"] 次のパートでは、円に動きを追加してください。\nGitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\n動画で見る ","description":"","tags":null,"title":"サウンドとカラー設定","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_06/index.html"},{"content":"ほとんどのゲームでは、これを「レベル」または「メインシーン」と呼びます。ここではそのように呼称します。\nシーンを「メイン」という名前の Node2D（で表示）で開始し、保存してください。\n背景の作成方法 以下の手順で操作してください。\nアイコンを使用して、新しい Sprite2D 子オブジェクトを追加してください。 このスプライトに「Background」という名前を付けます。 テクスチャとして Space_BG (2 frames) (64 x 64).png を適用します。 この画像には2つのフレームがあり、それぞれ64x64ピクセルのサイズです。画面全体に画像を敷き詰めたいので、まずは以下の設定から始めてください。\nOffset 設定で Centered(中央揃え) をオフにします。これにより、画像の左上隅が原点から始まるようになり、中心からは始まらなくなります。\nRegion 設定で Enabled をオンにし、幅を240、高さを320に指定します。これにより画像が画面サイズに合わせて拡大されます。\nテクスチャ 設定で Repeat(繰り返し) を Enabled(有効) に変更してください。これにより画像が画面全体に繰り返されるようになります。\n現在のシーンにプレイヤーを追加するには、Mainノードを選択して子シーンをインスタンス化ボタンをクリックします。\n背景をアニメーション化する方法 背景アニメーションを追加することでシーンに動きを与えることができます。フレームごとに region_rect プロパティを変更することでコード上で実現することも可能ですが、ここでは代わりに AnimationPlayer ノードを使いましょう。Main の子要素として追加してください。\nエディターウィンドウの下部にはアニメーションパネルが表示されます。ここには多くの情報が配置されていますので、その構成を確認していきます。\nクリックしてください。次に［新規アニメーション］を選択します。新しいアニメーションには名前をscrollと付けてください。［長さ］の値は2に設定し、［ループ再生］と［自動再生］ボタンを有効にします。\nAnimationPlayer は、制御したいプロパティを表す「トラック」を追加することで動作します。プレイヤーのタイムライン上では、「キーフレーム」を追加して特定の時点でそのプロパティに設定したい値を指定します。\nアニメーションにキーフレームを追加するには、インスペクターの各プロパティ横に表示された鍵アイコンをクリックします。スライダ（タイムライン上の青いマーカー）が時間0に位置していることを確認したら、まず Background(背景) を選択し、領域/矩形の横にある鍵ボタンをクリックします。新規トラックを作成するか確認されるので、指示に従ってください。すると、追加されたキーフレームを表す小さなドットとともに、新しいトラックがアニメーションパネルに追加されます。スライダを時間2まで移動させ、最後に領域/矩形プロパティの y 値を64に設定します。別のキーフレームを追加するには、該当するキーをクリックします。\nアニメーションの再生ボタンを押すと、プレイヤーの背後で背景がゆっくりとスクロール表示されるはずです。\n次のステップ メインシーンが完成し、敵キャラクターを追加する準備が整いました。次のステップでは、弾丸と同じ方法で単一の敵用シーンを作成し、その後複数回インスタンス化します。\n戻る 次へ ","description":"","tags":null,"title":"メインシーン","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_06/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 オブジェクトを別のオブジェクトの周りで「周回」（円軌道を描くように移動）させたい。\n解決策 これは初心者がよく抱く疑問で、三角関数をあれこれ試した後によく出てきます。答えは実はとてもシンプルです。\n軌道周回させるスプライトをメインスプライトの子ノードに配置します（ここでは「ピボット」と呼びます）。オフセット値を適用し、Pivotで回転させます。\nextends Node2D @export var rotation_speed = PI func _process(delta): $Sprite/Pivot.rotation += rotation_speed * delta この手法は3D空間でも同じように機能します。\nextends Node3D @export var rotation_speed = PI func _process(delta): $MeshInstance/Pivot.rotate_y(rotation_speed * delta) ","description":"","tags":null,"title":"円運動","uri":"/godot_recipes/4.x/ja/basics/rotation/index.html"},{"content":"課題 3Dで回転するキューブを作成したい。\n解決策 キューブを転がすのは見た目より難しいです。単に中心軸を中心に回すだけではうまくいきません：\n代わりに、キューブを底面のエッジを中心に回転させます。\nここが重要なポイントです。どの底面の縁でしょうか？ それは、キューブがどの方向に転がっているかによって異なります。\nこのレシピを作成するにあたり、異なる解決策を試しました。\n純粋数学 - 回転変換の計算と適用 AnimationPlayer - アニメーションを用いた回転およびオフセット制御 ヘルパーノード - RotationHelperとしてNode3Dを利用 すべて正常に動作しましたが、最後のオプションが最も柔軟で設定しやすいと感じたので、ここではその方法を採用します。\nノード設定 Cube: CharacterBody3D Pivot: Node3D Mesh: MeshInstance3D Collision: CollisionShape3D ヒント 衝突を検知するノードとして RigidBody3D、CharacterBody3D、または Area3Dを使用することも可能です。ただし、移動制御の方法には若干の違いが生じます。どのノードを選択するかは、ゲームで他にどのような挙動を必要とするかによって決めるべきです。このレシピでは、単に動きに焦点を当てています。\nデフォルトでは、すべてが座標 (0, 0, 0) を中心に配置されています。まず最初に行うことは、キューブの底面中央が CharacterBody3D の位置と一致するように全体をオフセットすることです。\nデフォルトのサイズが (1, 1, 1) の場合、以下のように設定します。メッシュノードと衝突判定ノードを両方とも (0, 0.5, 0) に移動し、その他は元のままにします。これでルートノードを選択すると、その位置がキューブの 底面 に対応します。\nこれでキューブを転がしたい場合、Pivotを「移動させたい方向」に0.5ユニット動かす必要があります。メッシュはオブジェクトに取り付けられているため、反対方向に同じ量だけ動かさなければなりません。例えば、右方向へ転がす場合（+X軸方向）、最終的に以下のコードになります。\n現在、ピボットノードは正しいエッジ上に配置されており、これを回転させるとメッシュ全体が一緒に回転します。\n移動スクリプト この動作は3つのステップに分かれています。\nステップ1 以下のように、先に示した2つのオフセットを適用します。Pivotを移動方向にシフトし、Meshをその反対方向にシフトします。\nステップ2 このステップでは、回転アニメーションを実装します。方向ベクトルと下方ベクトルの外積を用いて、回転軸を計算します。その後、Tween を使用してピボット要素の transform プロパティをアニメーションさせ、滑らかな回転効果を実現します。\nステップ3 アニメーションが終了したら、初期状態にリセットして次回の動作に備えます。キューブが選択方向に1単位移動し（サイズ1のキューブの場合）、かつピボットとメッシュが元の位置に戻るようにしたいということです。\nextends CharacterBody3D @onready var pivot = $Pivot @onready var mesh = $Pivot/MeshInstance3D var cube_size = 1.0 var speed = 4.0 var rolling = false func _physics_process(delta): var forward = Vector3.FORWARD if Input.is_action_pressed(\"ui_up\"): roll(forward) if Input.is_action_pressed(\"ui_down\"): roll(-forward) if Input.is_action_pressed(\"ui_right\"): roll(forward.cross(Vector3.UP)) if Input.is_action_pressed(\"ui_left\"): roll(-forward.cross(Vector3.UP)) func roll(dir): # Do nothing if we're currently rolling. if rolling: return rolling = true # Step 1: Offset the pivot. pivot.translate(dir * cube_size / 2) mesh.global_translate(-dir * cube_size / 2) # Step 2: Animate the rotation. var axis = dir.cross(Vector3.DOWN) var tween = create_tween() tween.tween_property(pivot, \"transform\", pivot.transform.rotated_local(axis, PI/2), 1 / speed) await tween.finished # Step 3: Finalize the movement and reset the offset. transform.origin += dir * cube_size var b = mesh.global_transform.basis pivot.transform = Transform3D.IDENTITY mesh.position = Vector3(0, cube_size / 2, 0) mesh.global_transform.basis = b rolling = false キューブのテクスチャが非対称の場合、転がすたびにリセットされることに気付くかもしれません。メッシュの回転を保持するには、以下を追加してください。\nステップ1では：\n翻訳を修正してください: mesh.translate(-dir) を mesh.global_translate(-dir) に変更してください。\nステップ3では：\nMesh の回転を保持するために 2 行追加してください。\n# Step 3: Finalize the movement and reset the offset. transform.origin += dir * cube_size var b = mesh.global_transform.basis # Save the mesh rotation. pivot.transform = Transform3D.IDENTITY mesh.position = Vector3(0, cube_size / 2, 0) mesh.global_transform.basis = b # Restore the mesh rotation. 衝突のチェック ゲームに障害物を導入する場合、移動前に衝突判定を行えます（他のグリッドベース移動方式と同様）。移動処理のステップ1の前に、レイキャストによる衝突チェックを追加してください。\n# Cast a ray before moving to check for obstacles var space = get_world_3d().direct_space_state var ray = PhysicsRayQueryParameters3D.create(mesh.global_position, mesh.global_position + dir * cube_size, collision_mask, [self]) var collision = space.intersect_ray(ray) if collision: return メモ 以下の方法も使用できます。RayCast3Dノードを使用する場合。ただし、チェックを実行する前に必ずforce_raycast_update()を呼び出すようにしてください。\nトランジションで遊んでみよう 使用するTransitionTypeを変更することで、キューブの転がり動作に様々な個性を持たせることができます。デフォルトはTween.TRANS_LINEARで、これにより移動全体を通じて一定速度が得られます。\n移行タイプを変更するだけで、全く異なる印象を得られます。例えば。\nvar tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN) プロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。https://github.com/godotrecipes/rolling_cube\n関連レシピ トランスフォーム ","description":"","tags":null,"title":"転がるキューブ","uri":"/godot_recipes/4.x/ja/3d/rolling_cube/index.html"},{"content":" 物理 Godot の物理ノードの使い方を学びます。\n目次 ： RigidBody2D で対象物を見る CharacterBody3D : 坂道で停止する RigidBody2D で目標位置へ移動 衝突レイヤーとマスク 動摩擦 RigidBody2D: ドラッグ＆ドロップ操作 CharacterとRigidBodyの相互作用 2Dジョイントを使う コンベアベルト アステロイド風物理演算（RigidBody2Dを使用） ","description":"","tags":null,"title":"物理","uri":"/godot_recipes/4.x/ja/physics/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 GodotのJoint2Dノードについて理解したい。\n解決策 ジョイントは、接続された物理オブジェクトの動きを制限するために使います。任意のジョイントノードには、PhysicsObject2D から拡張される2つのボディを結合が必要です。\nプロパティ これらのプロパティはすべての関節ノードに共通です。\nNode A and Node B：割り当てられた物理ボディ Bias：両オブジェクトが離れる方向に関節が引き寄せる速度。デフォルト値は 0 です Disable Collisions：接続されたオブジェクト間の衝突を考慮しないようにできます。デフォルトは true（有効）です 以下の3種類の Joint2D があります。すべての例で、1つの RigidBody2D がジョイントを介して StaticBody2D に接続されています。画面で「コリジョン形状を表示」が有効化されているため、ジョイントの表現を確認できます。\nPinJoint2D 「ピン接合」は、2つの部材を1点で接続し、自由に回転できるようにします。\nピン接合部の Softness プロパティにより、接続にある程度の「弾力」が与えられます。この値はデフォルト値の 0（移動不可）から最大値 16 まで設定できます。\nDampedSpringJoint2D この継手はスプリング状の力によって2つの部材を連結します。\n以下のプロパティでスプリングの挙動を調整できます。\nLength : 関節の最大許容長値 Rest Length : 外力や運動が作用していない状態における関節の長さ Stiffness : ばねの「伸びにくさ」、すなわち外力に対する抵抗強度を表す指標 Damping : ばねが「反発動作」を停止する速度特性を示すパラメータ GrooveJoint2D この関節は、接続された物体が直線的に移動するように制約します。\nデフォルトでは垂直方向に配置されますが、ノードを回転させることでこれを変更できます。\n以下のプロパティは溝の動作を制御します。\nLength：溝の全長。この最大距離を超える位置まで付属パーツは移動できません。 Initial Offset：溝に沿った起点となる「位置」です。 関連するレシピ ","description":"","tags":null,"title":"2Dジョイントを使う","uri":"/godot_recipes/4.x/ja/physics/joints_2d/index.html"},{"content":" オーディオ ゲームに効果音や音楽を追加するのに役立つレシピ集です。\n目次 ： オーディオマネージャー ","description":"","tags":null,"title":"オーディオ","uri":"/godot_recipes/4.x/ja/audio/index.html"},{"content":" シェーダー 魔法のような高度な処理が生む、クールなエフェクトの世界。\n目次 ： シェーダー入門編 シェーダーとの連携 グレースケール（モノクロ）シェーダー ブラーシェーダー ","description":"","tags":null,"title":"シェーダー","uri":"/godot_recipes/4.x/ja/shaders/index.html"},{"content":"課題 RTS(リアルタイムストラテジー)のように、複数ユニットを選択するためにクリック＆ドラッグしたい。\n解決策 リアルタイムストラテジー（RTS）ゲームでは、複数ユニットに同時に命令する事があります。一般的な操作手法として、対象ユニットをマウスでクリックしてドラッグすることで選択範囲を決定。またユニットを選択したら、マップ上をクリックすることで移動コマンドを実行させる事が多いです。\n以下に目指すべき例を示します。\nユニット設定 この機能を実際に試すには、基本的なRTSスタイルのユニットが必要です。これらのユニットはターゲットに向かって移動し、互いに衝突しないように設計されています。チュートリアルではこの点について詳しく説明しません。カスタムRTSユニット作成のベースとして使いたい場合は、ユニットスクリプトにコメントが付いています。プロジェクトをダウンロードするためのリンクは以下の通りです。\nワールドのセットアップ ユニット選択の処理はワールド内で行います。まず「World」という名前のNode2Dオブジェクトを作成し、その中にUnitインスタンスを追加してください。Worldノードにスクリプトをアタッチし、以下の変数を設定してください。\nextends Node2D var dragging = false # Are we currently dragging? var selected = [] # Array of selected units. var drag_start = Vector2.ZERO # Location where drag began. var select_rect = RectangleShape2D.new() # Collision shape for drag box. ※ボックスを描画した後は、その内部にどのユニットが位置しているかを確認する方法が必要となります。 RectangleShape2Dを使用すると物理エンジンに問い合わせて、衝突した対象を確認できます。\nボックスの描画方法 この操作にはマウスの左ボタンを使用します。クリックすることでドラッグが開始され、指を離すと終了します。この作業中に、視認性のために長方形を描いていきます。\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: # If the mouse was clicked and nothing is selected, start dragging if selected.size() == 0: dragging = true drag_start = event.position # If the mouse is released and is dragging, stop dragging elif dragging: dragging = false queue_redraw() if event is InputEventMouseMotion and dragging: queue_redraw() func _draw(): if dragging: draw_rect(Rect2(drag_start, get_global_mouse_position() - drag_start), Color.YELLOW, false, 2.0) ユニットの選択方法 選択ボックスが作成できたら、その内部に位置するユニットを特定する必要があります。ボタンを放してドラッグ操作が終了した際には、物理空間クエリを実行して対象のユニットを検索する必要があります。なお、対象となるユニットはCharacterBody2Dですが、Area2Dやその他のボディタイプでも問題ありません。\nPhysicsDirectSpaceState2D.intersect_shape()を使用してユニットを検出します。これには形状（ここでは矩形）と変換行列（位置）が必要となります。詳細はGodotドキュメントを参照してください。\nelif dragging: dragging = false queue_redraw() var drag_end = event.position select_rect.extents = abs(drag_end - drag_start) / 2 まず、ボタンを離した瞬間の位置座標を取得し、この値を使ってRectangleShape2Dの extents を設定します（注意点：extentsは矩形の中心から計測されるため、実際の幅・高さの半分となります）。\nvar space = get_world_2d().direct_space_state var query = PhysicsShapeQueryParameters2D.new() query.shape = select_rect query.collision_mask = 2 # Units are on collision layer 2 query.transform = Transform2D(0, (drag_end + drag_start) / 2) selected = space.intersect_shape(query) これで物理状態への参照を取得し、PhysicsShapeQueryParameters2Dを使用して形状クエリを設定できます。ここでは対象の形状を指定するとともに、ドラッグ操作中のエリアの中心座標を基準にしてクエリの変換行列を定義します。intersect_shape()を呼び出した後の結果は、Dictonary 配列として返され、以下のような形式になります。\n[{ \"rid\": RID(4093103833089), \"collider_id\": 32145147326, \"collider\": Unit2:\u003cCharacterBody2D#32145147326\u003e, \"shape\": 0 }, { \"rid\": RID(4123168604162), \"collider_id\": 32229033411, \"collider\": Unit3:\u003cCharacterBody2D#32229033411\u003e, \"shape\": 0 }] 各collider項目はそれぞれユニットへの参照であるため、これを使用すれば選択通知を行い、アウトラインシェーダーを有効にできます。\nfor item in selected: item.collider.selected = true ユニットへの指揮 最終的に、画面上の任意の位置をクリックすることで、選択したユニットに移動コマンドを発行できます。\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: # If the mouse was clicked and nothing is selected, start dragging if selected.size() == 0: dragging = true drag_start = event.position # Otherwise a click tells the selected units to move else: for item in selected: item.collider.target = event.position item.collider.selected = false selected = [] ここでの else 条件は、selected が 0 より大きいときにマウスをクリックした場合にトリガーされます。各項目の target を設定した後、ユニットを選択解除することで、再度最初から開始できるようにしています。\nまとめ このテクニックはリアルタイムストラテジーゲーム（RTS）やその他のジャンルのゲームに応用できます。以下から完全版プロジェクトをダウンロードして、ゲームを作る際に活用してください。\nプロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。 https://github.com/godotrecipes/multi_unit_support\n関連レシピ マウス入力 ","description":"","tags":null,"title":"マウスドラッグでユニットを選択","uri":"/godot_recipes/4.x/ja/input/multi_unit_select/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 3D空間でWASD操作を使用している場合、カメラが回転すると方向感覚を失いやすくなります。プレイヤー視点（つまりカメラ）の前進方向と、ゲーム内ワールド内オブジェクトの前方方向、どちらを基準にすべきでしょうか？\n解決策 このケースはさまざまなシナリオに適用できますが、ここでは転がるキューブレシピを具体例として説明します。\nキューブのスクリプトには、移動に関する以下のコードが含まれています。\nfunc _physics_process(_delta): var forward = Vector3.FORWARD if Input.is_action_pressed(\"up\"): roll(forward) if Input.is_action_pressed(\"down\"): roll(-forward) if Input.is_action_pressed(\"right\"): roll(forward.cross(Vector3.UP)) if Input.is_action_pressed(\"left\"): roll(-forward.cross(Vector3.UP)) ご覧のとおり、これはグローバル座標系の方向ベクトルを使用しているため、カメラを回転させると「上」がカメラビューで前進しているように見えなくなります。カメラを180度回転させれば、すべてが反転してしまいます！\n以下のようにカメラの前方ベクトルを使用するように変更できます。\nvar forward = -camera.transform.basis.z.normalized() 一部の設定ではこれでも問題ありませんが、キューブに関してはまったく機能しません：\nキューブは4つの基本方向（前後・左右・上下）にのみ移動できます。このため、カメラの前方ベクトルを取得し、どの軸方向に最も近いかを確認する必要があります。+X、-X、+Z、または -Z のいずれかです。\nこれは、Vector3.max_axis()関数を使用してみます。この関数は、ベクトルの成分のうち最も大きい値を返します。値は正または負になる可能性があるため、まずabs()を適用して絶対値に変換します。\n一旦最大の大きさの軸が特定されれば、以下のように forward ベクトルを調整できます。\nfunc _physics_process(_delta): var forward = Vector3.FORWARD if camera: forward = Vector3.ZERO var cam_forward = -camera.transform.basis.z.normalized() var cam_axis = cam_forward.abs().max_axis() forward[cam_axis] = sign(cam_forward[cam_axis]) if Input.is_action_pressed(\"up\"): roll(forward) if Input.is_action_pressed(\"down\"): roll(-forward) if Input.is_action_pressed(\"right\"): roll(forward.cross(Vector3.UP)) if Input.is_action_pressed(\"left\"): roll(-forward.cross(Vector3.UP)) このクリップでは、移動するために「w」キーのみを押しています。\n関連レシピ 転がるキューブ カメラジンバル この動画が気に入ったら？ ","description":"","tags":null,"title":"移動をカメラに合わせる","uri":"/godot_recipes/4.x/ja/3d/move_with_camera/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n今回の連載では、一人称視点キャラクターの作成方法について解説します。前回作成したCSGベースのレベルを歩行テスト用のステージとして使用し、移動機能を検証していきます。\nキャラクターシーン FPSや類似ゲームにおいて、プレイヤーに「キャラクターの目を通して世界を見ている」という臨場感を与えたいものです。素晴らしい点は、最初はモデル自体が必ずしも必要ではないという点です。\nCharacterBody3D オブジェクトを用意します。このオブジェクトに以下の2つの CollisionShape ノードを追加してください。「ボディ用」と「足用」です。また、カメラも必要ですが、回転処理には注意が必要です。キャラクターはY軸に沿ってのみ回転し、一方でカメラのみがX軸方向に回転する必要があります（上下方向の視点移動のため）。これを実現するために、新たに Node3D オブジェクトを作成し、「ピボット」と呼ぶことにします。このピボットノードにCameraを追加してください。\n「Body」衝突形状はプレイヤーの身体を表現するものです。ここでは CapsuleShape を使用します（X 軸を中心に90度回転させて設定）。半径を0.5、高さを1に設定してください。\nCapsuleShapeを使用する際の問題点は、底面が丸みを帯びている点です。これは小さな障害物を自然に乗り越える際には便利ですが、同時に、プレイヤーが表面の縁に立った場合に「不自然に転がり落ちる」という問題を引き起こします。この問題を解決するには、衝突判定用に｢BoxShape｣を使用することが有効です。ボックスの範囲は(0.4, 0.1, 0.4)とし、カプセルシェイプの底面より少し上に位置するように配置します。これにより、物体を乗り越えるための丸みを帯びた底面を確保しつつ、箱型形状のおかげで段差から滑り落ちるのを防ぐことができます。\nPivotノードを少し上に移動させ、カメラが身体の中心から外れて向かないようにします。\n移動方法 移動関連のコードは、本シリーズで先に紹介した サードパーソン型キャラクターと同様です。まずは変数宣言から始めてください。\nextends CharacterBody3D @onready var camera = $Pivot/Camera var gravity = -30 var max_speed = 8 var mouse_sensitivity = 0.002 # radians/pixel var velocity = Vector3() メモ mouse_sensitivity の値は、マウスの移動距離（ピクセル単位）を角度回転に変換します。つまり、マウスが1ピクセル移動するごとに、0.002ラジアン（約0.1度）だけ回転します。\nfunc get_input(): var input_dir = Vector3() # desired move in camera direction if Input.is_action_pressed(\"move_forward\"): input_dir += -global_transform.basis.z if Input.is_action_pressed(\"move_back\"): input_dir += global_transform.basis.z if Input.is_action_pressed(\"strafe_left\"): input_dir += -global_transform.basis.x if Input.is_action_pressed(\"strafe_right\"): input_dir += global_transform.basis.x input_dir = input_dir.normalized() return input_dir 入力を検知した際は、体の向いている方向に移動させたい。\n続いて入力値を加算し、最終的な方向ベクトルを返します。\nfunc _unhandled_input(event): if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: rotate_y(-event.relative.x * mouse_sensitivity) $Pivot.rotate_x(-event.relative.y * mouse_sensitivity) $Pivot.rotation.x = clamp($Pivot.rotation.x, -1.2, 1.2) また、カメラ回転用にマウス移動の検出も必要です。前述のとおり、水平方向のマウス移動は身体全体をY軸方向に回転させ、垂直方向の移動では補助ノードをX軸方向に回転させます。\nまた、カメラが上下逆さまになるのを防ぐため、垂直方向の回転も制限する必要があります。\nfunc _physics_process(delta): velocity.y += gravity * delta var desired_velocity = get_input() * max_speed velocity.x = desired_velocity.x velocity.z = desired_velocity.z velocity = move_and_slide(velocity, Vector3.UP, true) _physics_process()関数内では、desired_velocityを取得します。これはget_input()によって返される方向ベクトルで、これにmax_speedを掛けることで速度ベクトルの長さを調整しています。重力によって設定されているvelocity.yは変更したくないため、入力値に基づいてxとz成分のみを設定しています\nmove_and_slide()のstop_on_slopeパラメーターにもtrueを設定しています。これにより、スロープに立っても滑り落ちるのを防ぎます。\n動作テストを行い、すべてが期待通りに機能していることを確認してください。\n武器の追加方法 一人称視点のキャラクターは通常、何らかのアイテムを持っているか、少なくとも手ぶらの場合、その正面に何かが見えているものです。今回は以下のアートパックからショットガンモデルを使用します。\nKenney Weapon Pack\nPivot ノードに MeshInstance を追加し、アートパックに含まれる shotgun.obj モデルを使用してください。このモデルはプレイヤーのスケールに対して小さすぎることが確認できるので、「_Scale」プロパティを (8, 8, 8) に設定してください。\nモデルの位置を調整してカメラの「向こう側」に投影されるようが必要です。位置合わせが難しい場合は「ビュー」メニューをクリックし、「2つのビューポート」を選択します。下側のウィンドウで［Camera］を選択し、「プレビュー」をクリックします。上側では、銃が正しく見えるまで移動させることができます。\nまとめ これで基本的な一人称視点キャラクターコントローラーが完成しました。今後のレッスンで学ぶように、機能や動作を追加することで、さまざまなゲームジャンルに対応できる優れた基盤となるはずです。\nこのレッスンの動画バージョンはこちらでもご覧いただけます。 ","description":"","tags":null,"title":"一人称キャラクター","uri":"/godot_recipes/4.x/ja/g101/3d/101_3d_07/index.html"},{"content":"プレイヤーが射撃できるようになったので、今度は撃つ対象を与えます。\nシーンの設定 敵キャラには Area2D を使用します。これは、プレイヤーの弾丸との重複判定や、直接衝突検出する必要があるためです。\n以下に必要となるノードを列挙します。\nEnemy: Area2D Sprite2D CollisionShape2D AnimationPlayer MoveTimer: Timer ShootTimer: Timer ※「インスペクター」横の ノード タブをクリックします。グループ セクションに「enemies」と入力し、追加 をクリックしてください。弾丸コードで指定したように、これは「enemies」グループ内のオブジェクトを探します。\nスプライトのテクスチャにBon_Bon (16 x 16).pngを追加し、アニメーション/フレーム数を4に設定してください。\n以前と同じように矩形の衝突形状を追加し、サイズを調整してください。タイマーノードの両方でワンショットを有効にします。\nAnimationPlayer において、「バウンス」というアニメーションを追加し、ループ再生と自動再生を設定します。アニメーションパネルの下部にあるスナップ値を 0.05 に設定してください。\nスプライトノードを選択し、テクスチャ と フレーム数 の横にあるキーアイコンをクリックしてトラックを作成します。これは、後でこれらのプロパティに異なる値を使用する「爆発」アニメーションを追加するためです。\n次に、必要な各フレーム値を設定していきます。まずは10ミリ秒ごとに以下の順番でフレームを設定します。2, 1, 0, 3。最後にもう一度0を設定してすぐ後ろに配置します。これにより、スプライトが徐々に大きくなり、最後に少し弾むような「脈動」アニメーションが完成します。このセットアップは以下のようになるはずです。\n再生ボタンを押して実際に動作を確認してみてください。必要に応じて調整していただいても結構です。\n次に、「explode」というアニメーションを追加してください。長さを 0.4 秒に設定してください。\nスプライトのテクスチャを Explosion (16 x 16).png に変更し、そのプロパティにキーフレームを適用します。この画像は敵キャラ画像と異なるフレーム数を持っているため、Hフレームも 6 に変更し、同様にキーフレームを設定が必要です。\n現在のキーフレームを時間 0 時点で0に、時間 0.4 時点で5に設定してください。アニメーションを実行して動作を確認してください。\n敵キャラ用スクリプト 敵ユニットは画面上部のグリッド状に配置されて出現します。一定時間後にプレイヤー方向へ降下を開始し、破壊されない場合は画面上部に復帰します。また、定期的にプレイヤーに対して攻撃を仕掛けてきます。\nスクリプトを追加し、変数から始めてください。\nextends Area2D var start_pos = Vector2.ZERO var speed = 0 @onready var screensize = get_viewport_rect().size 変数 start_pos は、敵の初期位置を追跡するために使用され、移動後に元の位置に復帰できるようにします。これは敵が生成される際に設定し、start() 関数を呼び出すときに行います。\nfunc start(pos): speed = 0 position = Vector2(pos.x, -pos.y) start_pos = pos await get_tree().create_timer(randf_range(0.25, 0.55)).timeout var tween = create_tween().set_trans(Tween.TRANS_BACK) tween.tween_property(self, \"position:y\", start_pos.y, 1.4) await tween.finished $MoveTimer.wait_time = randf_range(5, 20) $MoveTimer.start() $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() 敵キャラを出現させる際、この関数を呼び出して画面内の位置ベクトルを渡します。ここで重要なのは、実際には画面上部に配置される点です（y座標が負値）。これは、Tweenアニメーションを使用して画面に出現する様子を表現するためです。また、2つのタイマー値はランダム化しており、すべての敵が同時に移動・攻撃を開始しないように制御しています。\n両方のタイマーの timeout シグナルを接続してください。\nfunc _on_timer_timeout(): speed = randf_range(75, 100) func _on_shoot_timer_timeout(): $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() タイマーが切れた時点で移動を開始でき、同時に射撃も行いますが、まだ弾丸を作成していないため、その部分は後で実装します。現在speed変数を変更しているので、これを使って移動できるようになります。\nfunc _process(delta): position.y += speed * delta if position.y \u003e screensize.y + 32: start(start_pos) もし speedが 0 でない場合、敵が画面を下方向に移動するのが見えるでしょう。画面下から消えてしまうと、最初からやり直しになります。\nすでに弾丸シーンのコードで敵に当たったときに explode() を呼び出す処理は書いてあるので、これも追加してください。\nfunc explode(): speed = 0 $AnimationPlayer.play(\"explode\") set_deferred(\"monitoring\", false) died.emit(5) await $AnimationPlayer.animation_finished queue_free() この関数では、移動を停止し、爆発アニメーションを再生した後、処理が終了した時点で敵オブジェクトを削除します。set_deferred() 呼び出しにより、敵オブジェクトの モニタリング 機能を確実に無効化しています。これは、爆発中の敵に別の弾丸が当たらないようにするためです。\nスクリプトの冒頭に died シグナルを追加してください。\nsignal died このシグナルを使って、メインシーンにプレイヤーが得点を獲得したことを通知します。\n敵キャラクターの出現 それでは、Main シーンに移動して、これらの敵キャラクターをゲームに追加してください。Mainシーンにスクリプトを追加し、まず敵キャラ用シーンを読み込むことから始めます。\nextends Node2D var enemy = preload(\"res://enemy.tscn\") var score = 0 通常、敵はゲーム開始ボタンを押してプレイを開始するまで出現しませんが、まだその段階に達していないので、ここではすぐに敵を出現させます。\nfunc _ready(): spawn_enemies() func spawn_enemies(): for x in range(9): for y in range(3): var e = enemy.instantiate() var pos = Vector2(x * (16 + 8) + 24, 16 * 4 + y * 16) add_child(e) e.start(pos) e.died.connect(_on_enemy_died) これにより、27体の敵キャラクターが生成され、画面上部半分にグリッド状に配置されます。また、各敵キャラの died(死亡) シグナルを適切に接続する必要があるため、この機能を実装する関数を作成します。\nfunc _on_enemy_died(value): score += value まだスコアを表示する方法がありませんが、すぐに対応します。\nシーンを再生すると、画面上部から敵の集団が次々と出現し、定期的に画面下へ落ちていく様子が確認できるはずです。次に、これらに攻撃動作を追加していきます。\n戻る 次へ ","description":"","tags":null,"title":"敵兵","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_07/index.html"},{"content":"バグ修正について まず最初に取り組むべきは、メニューシステムの不具合修正です。「スタート」ボタンを押すと新しいゲームが開始されますが、画面が切り替わる途中で再びボタンを押せる状態になってしまいます。試しに「連打」してみてください。大変なことになりますよ！\nこの問題は、画面遷移中にボタンを無効化することで解決できます。すべてのボタンが「buttons」グループに属しているため、call_group() 関数を使えば簡単に実装できます。\n以下に更新済みの BaseScreen.gd を示します。\nextends CanvasLayer @onready var tween = $Tween func appear(): get_tree().call_group(\"buttons\", \"set_disabled\", false) tween.interpolate_property(self, \"offset:x\", 500, 0, 0.5, Tween.TRANS_BACK, Tween.EASE_IN_OUT) tween.start() func disappear(): get_tree().call_group(\"buttons\", \"set_disabled\", true) tween.interpolate_property(self, \"offset:x\", 0, 500, 0.5, Tween.TRANS_BACK, Tween.EASE_IN_OUT) tween.start() スコアとレベルについて スコアが上がるにつれ、ゲームの難易度も適切に上昇させる必要があります。つまり、得点を獲得した際には一定の閾値（circles_per_level）に達したかどうかを確認する必要が生じます。また、ジャンプで円に乗る以外にも、得点を獲得する手段が存在するかもしれません。これを管理しやすくするため、メインスクリプト内でスコア変数にsetgetメソッドを定義します。\nvar score = 0 setget set_score var level = 0 また、new_game()関数でそのメソッドを使用するよう更新してください。\nfunc new_game(): self.score = 0 level = 1 同じように_on_Jumper_captured()内のスコア変更処理も修正し、HUD更新ロジックを新規メソッドset_score()に移動させます。\nfunc _on_Jumper_captured(object): $Camera2D.position = object.position object.capture(player) call_deferred(\"spawn_circle\") self.score += 1 func set_score(value): score = value $HUD.update_score(score) if score \u003e 0 and score % settings.circles_per_level == 0: level += 1 $HUD.show_message(\"Level %s\" % str(level)) このゲームを試してみると、5ポイントに達した時点で画面に「レベル2」のメッセージが表示されるはずです。\n移動する円 レベルアップに伴う難易度上昇の仕組みとして、サークルの動きを変化させる方法を採用します。既に静的タイプと限定的移動可能なタイプという複数のサークル種類が存在していますが、これらはいずれも動的な動きを可能にするべき機能を備えているため、新たなタイプを追加するわけではありません。むしろ、これは任意のサークルに付与できるプロパティとして実装されます。\nシーンCircleを開き、Tweenノードを「MoveTween」という名前で追加してください。これをサークルスクリプトの先頭に追加してください。\n@onready var move_tween = $MoveTween var move_range = 100 # Distance the circle moves. var move_speed = 1.0 # The circle's movement speed. move_rangeが 0 の場合、動かない円が表示されます。ここでデフォルト値を 100 に設定しておくと、動作確認が簡単に行えます。\n移動処理を管理するために、MoveTweenを開始します。処理が完了したら、tween_completedシグナルを利用して逆方向に再始動させます。\nこれは移動を開始するコードです。tween_completedシグナルをこの関数に接続してください。\nfunc set_tween(object=null, key=null): if move_range == 0: return move_range *= -1 move_tween.interpolate_property(self, \"position:x\", position.x, position.x + move_range, move_speed, Tween.TRANS_QUAD, Tween.EASE_IN_OUT) move_tween.start() 最後に、init() 関数の末尾に set_tween() を追加し、実際に動作を確認してみます。\nGitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\n動画で見る ","description":"","tags":null,"title":"動く円","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_07/index.html"},{"content":"敵が攻撃できるようになったので、今度は彼らが撃つ対象を与えます。\n敵弾シーン 以前プレイヤー用の弾丸を作成したときと同じように、新しいEnemyBulletシーンを作成してください。ここでは詳細な手順は割愛しますが、行き詰まった場合はその部分を参照してください。唯一の違いは、代わりにEnemy_projectile (16 x 16).png画像を使用できる点です。\nスクリプトは少し異なります。\nextends Area2D @export var speed = 150 func start(pos): position = pos func _process(delta): position.y += speed * delta VisibleOnScreenNotifier2DとArea2Dの screen_exited シグナルと area_entered シグナルをそれぞれ接続してください。\nfunc _on_visible_on_screen_notifier_2d_screen_exited(): queue_free() func _on_area_entered(area): if area.name == \"Player\": queue_free() ※プレイヤーへのヒットは検出していますが、現時点では何も処理していません。ダメージを与える仕組みを追加した時点で、この問題に対処します。\n敵への射撃を追加 敵のスクリプト上部で新しい弾丸をロードします。\nvar bullet_scene = preload(\"res://enemy_bullet.tscn\") 次に射撃関数を書き換えます。\nfunc _on_shoot_timer_timeout(): var b = bullet_scene.instantiate() get_tree().root.add_child(b) b.start(position) $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() Main シーンを再生し直すと、ランダムな敵の弾丸が出現するはずです。\n戻る 次へ ","description":"","tags":null,"title":"敵の射撃","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_08/index.html"},{"content":"課題 アーケード／映画のようなスタイルで飛行する3D宇宙船を作りたい。リアルな物理演算は求めず、ドッグファイト主体の『スター・ウォーズ』風の宇宙戦闘スタイルで。\n解決策 この機能を実現するため、船のモデルには CharacterBody3D クラスを使用します。ピッチ（前後傾斜）、ロール（横転）、ヨー（回転）の3軸入力により、それぞれ対応する 軸 を中心にボディの基底ベクトルが回転します。移動方向は常に前方を指すようになっています。\nメモ これはRigidBody3Dを使用することで実現可能で、同様の結果が得られます。以下にリンクするサンプルプロジェクトでは、リジッドボディ版も含まれていますので、ぜひご参照ください。\nアセット 宇宙船モデルは以下のアセットパックに含まれています。\nQuaternius 制作「Ultimate Spaceships Pack」\n「処刑人」級の艦船モデルを選択しました。\nご自由にお好みのデザインをお選びください。\n設定手順 操作対象の宇宙船に対応する gltf ファイルを選択し、インポートタブをクリックしてください。ルートタイプを CharacterBody3D に変更します。次に『再インポート』をクリックします。その後、gltfファイルをダブルクリックすると、新しいシーンが生成され、その中に CharacterBody3D をルートとする子オブジェクトとして MeshInstance が表示されます。ボディに CollisionShape3Dを追加してください。\nプロジェクト設定 \u003e インプットマップ にて、以下の入力を設定してください。\nroll_right / roll_left （ロール操作） pitch_up / pitch_down （ピッチ操作） yaw_right / yaw_left （ヨー操作） throttle_up / throttle_down （スロットル操作） キーまたはコントローラー入力を割り当てることができます。アナログスティックの入力が最も適しています。\n移動 スクリプトを起動するには、前進動作を処理します。スロットルボタンを滑らかに押すと、速度が段階的に増減します。\nextends CharacterBody @export var max_speed = 50.0 @export var acceleration = 0.6 var forward_speed = 0 func get_input(delta): if Input.is_action_pressed(\"throttle_up\"): forward_speed = lerp(forward_speed, max_speed, acceleration * delta) if Input.is_action_pressed(\"throttle_down\"): forward_speed = lerp(forward_speed, 0, acceleration * delta) func _physics_process(delta): get_input(delta) velocity = -transform.basis.z * forward_speed move_and_collide(velocity * delta) テスト用シーンを作成し、Camera3D コンポーネントを試してみます。固定カメラを使用するか、追従カメラ を採用することもできます。宇宙船が加速と減速を適切に行うことを確認した上で、次のステップに進んでください。\n回転角度設定 現在、3軸方向の回転処理が対応可能になりました。以下の変数をスクリプトの先頭に追加してください。\n@export var pitch_speed = 1.5 @export var roll_speed = 1.9 @export var yaw_speed = 1.25 var pitch_input = 0.0 var roll_input = 0.0 var yaw_input = 0.0 三軸速度は艦船の「操作性」に直接影響を及ぼします。最適なフライトのために、ぜひ試行錯誤してみてください。\n次に、get_input() 関数に以下の行を追加して3軸入力を取得します。\npitch_input = Input.get_axis(\"pitch_down\", \"pitch_up\") roll_input = Input.get_axis(\"roll_right\", \"roll_left\") yaw_input = Input.get_axis(\"yaw_right\", \"yaw_left\") Finally, we need to rotate the ship’s Basis according to the inputs. Note how each input affects one axis of rotation:\ntransform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta) transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta) transform.basis = transform.basis.orthonormalized() 改善点 現在の回転モーションは少し「ぎこちない」感じがします。船が瞬時に回転を開始したり停止したりするため、やや不自然な動きになっています。これは lerp() 関数を使うことで改善できます。さらに、コントロールの「浮遊感」を調整する設定変数を設けることでより自然な挙動を実現できるでしょう。\n@export var input_response = 8.0 以下の内容に従って、get_input() 内の 3 軸入力を変更してください。\npitch_input = lerp(pitch_input, Input.get_axis(\"pitch_down\", \"pitch_up\"), input_response * delta) roll_input = lerp(roll_input, Input.get_axis(\"roll_right\", \"roll_left\"), input_response * delta) yaw_input = lerp(yaw_input, Input.get_axis(\"yaw_right\", \"yaw_left\"), input_response * delta) 今は停止や方向転換時に、わずかな慣性が働くようになっています。\nロール/ヨー軸の連動設定 この操作体系には1つの問題点があります。それは操作性に難がある点です。ヨーの入力に別途スティックを必要とするため、特に射撃や他のコントロールと組み合わせた場合、スムーズな操作が困難になります。多くのゲームでは、この問題を解決するためにロール入力と連動して少量のヨー回転も発生させる仕様にしています。これを実装するには、yaw_speedをroll_speedの1/4～1/2程度に設定するのが適切です。\nget_input() 関数内で、yaw_inputを取得する行を以下のように変更してください：\nyaw_input = roll_input ロールとヨーの速度を調整することで、ゲームプレイに変化をもたらす楽しい実験ができます。例えば、ヨーを主要軸としロールを小さくした場合、どのような挙動になるでしょうか？他の軸と連動させるとどう変わるでしょう？もしゲームに複数種類の船が登場するなら、それぞれ異なる値を設定することで、飛行スタイルや性能にバリエーションを持たせることができます。\nまとめ これで準備完了！飛行できます！このコントローラーは、思い描く宇宙ゲームを始めるのに最適なスタート地点です。さらに機体やエフェクトを何個か追加すれば、いよいよ本番モードに突入できます。\n全スクリプト 以下に全てのスクリプトを示します。\nextends CharacterBody3D @export var max_speed = 50.0 @export var acceleration = 0.6 @export var pitch_speed = 1.5 @export var roll_speed = 1.9 @export var yaw_speed = 1.25 # Set lower for linked roll/yaw @export var input_response = 8.0 var forward_speed = 0.0 var pitch_input = 0.0 var roll_input = 0.0 var yaw_input = 0.0 func get_input(delta): if Input.is_action_pressed(\"throttle_up\"): forward_speed = lerp(forward_speed, max_speed, acceleration * delta) if Input.is_action_pressed(\"throttle_down\"): forward_speed = lerp(forward_speed, 0.0, acceleration * delta) pitch_input = lerp(pitch_input, Input.get_axis(\"pitch_down\", \"pitch_up\"), input_response * delta) roll_input = lerp(roll_input, Input.get_axis(\"roll_right\", \"roll_left\"), input_response * delta) # yaw_input = lerp(yaw_input, Input.get_axis(\"yaw_right\", \"yaw_left\"), input_response * delta) yaw_input = roll_input func _physics_process(delta): get_input(delta) transform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta) transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta) transform.basis = transform.basis.orthonormalized() velocity = -transform.basis.z * forward_speed move_and_collide(velocity * delta) 関連レシピ 補間カメラの実装 –\u003e\nプロジェクトのダウンロード プロジェクトコードはこちらでダウンロードできます。https://github.com/godotrecipes/3d_spaceship\n","description":"","tags":[],"title":"アーケード風宇宙船","uri":"/godot_recipes/4.x/ja/3d/spaceship/index.html"},{"content":"ゲームの最後の主要コンポーネントは ユーザーインターフェース (UI) です。プレイヤーにスコアやその他の情報を表示する方法が必要になります。これを実現するために、Godotが提供する各種 Control ノードを使用します。これらはUI構築用に設計された専用ノードです。\nユーザインタフェースシーン シーンの開始には MarginContainer を使用し、名前は UI としてください。\nコンテナノード はControlノードの一種で、子要素のサイズや位置を制御するように設計されています。これを使用することで、手動で調整する手間をかけずにコントロールノードを簡単に配置・移動できます。MarginContainerを使用すると、その子要素が端に近づきすぎるのを防ぐことができます。\nインスペクターのテーマオーバーライド/定数セクションで、4つのマージン値をすべて 10 に設定します。その後、ビューポート上部のメニューバーで、アンカーをトップワイドプリセットに設定してください。\n次に、HBoxContainer を追加してください。このコンテナは子要素を水平方向に配置します。その上に、船のシールドレベルを表示するTextureProgressBar を追加し、「ShieldBar」と名前を付けます。\n申し訳ありませんが、進捗バーとして使用できる適切な画像がアートパックにありません（1つ存在はしますが、使い勝手の良いフォーマットではありません）。代わりに以下の2つの画像を使用します。1つは緑色のプログレスバー、もう1つは白い枠線です。これらをプロジェクトフォルダに保存してください。\nテクスチャ設定セクションで、背景画像のドラッグ先を「Progress」に、前景画像のドロップ先を「Under」に指定します。まず気づくのは、デフォルトサイズが非常に小さいことです。最初にレイアウト設定で「カスタム最小サイズ」を (80, 16) に設定してください。オレンジ色の選択枠は大きくなりますが、画像は変わりません。これは単に画像を延ばすと見た目が悪くなるためです。代わりに、「ナインパッチストレッチ」チェックボックスを有効にし、4つの「伸縮マージン値」をすべて 3 に設定します。\nこれで長くて塗りつぶされていないバーが表示されているはずです。塗りつぶされた状態でどのように見えるかを確認するには、「Range」セクションの「Value」プロパティを 0 から 100 の間の任意の値に変更してください。\n右側にはスコアを表示したいです。単にLabelノードを使ってフォントを追加するだけでもできますが、それでは面白みがありません。アートパックには、代わりに使用できる美しいピクセル数字セットが含まれています。適切に表示するためには、少しコーディングして数値を切り出す必要があります。\nスコアカウンター 新しいシーンを開始し、HBoxContainerを追加してください。ScoreCounter(スコアカウンター)と名前を付け、レイアウト設定をワイド・トップ配置に設定し、整列方法を「末尾」に設定してください。さらに、テーマのオーバーライド/定数/分離幅を 0 に設定します（プロパティ名の横にあるチェックボックスを必ず有効化してください）。\nこのコンテナでは、各数字を表示する TextureRectノードの文字列を配置します。まずは1つ追加し、それを複製する方法から始めてください。\nTextureRect Digit0という名前を付けます。テクスチャセクションで「新規AtlasTexture」を選択し、ボックスをクリックして開きます。Number_font (8 x 8).pngファイルをアトラスプロパティにドラッグし、領域を (32, 8, 8, 8) に設定します。伸縮モードは「アスペクト比を維持（中心固定）」に設定してください。\nDigit0ノードを選択し、Ctrl + D を7回押してノードの複製を作成します。この手順完了後に表示されるべき画面例を以下に示します。\n問題が発生しました。TextureRect を8つの個別コピーに複製したにもかかわらず、すべてのインスタンスが同じ AtlasTexture をテクスチャプロパティで共有しています。つまり、領域を変更して別の数字を表示しようとすると、すべての数字が同時に変わってしまうのです。\nこれはなぜかというと、Resourceオブジェクト（例えばTexture）はメモリにロードされてから共有されるからです。実際には単一のテクスチャしか存在しないため、このアプローチは非常に効率的です - 同じ画像を何度も読み込んでメモリを浪費するのを防げます。ただし、特定のノードをユニークにする場合は明示的に指定します。\n各ノードについて、AtlasTexture の隣にある下向き矢印をクリックし、「ユニーク化」を選択します。\nこれから、ScoreCounter にスクリプトを追加してください。このスクリプトは、表示する必要のある数字に応じて適切な リージョン 値を選択します。\nextends HBoxContainer var digit_coords = { 1: Vector2(0, 0), 2: Vector2(8, 0), 3: Vector2(16, 0), 4: Vector2(24, 0), 5: Vector2(32, 0), 6: Vector2(0, 8), 7: Vector2(8, 8), 8: Vector2(16, 8), 9: Vector2(24, 8), 0: Vector2(32, 8) } func display_digits(n): var s = \"%08d\" % n for i in 8: get_child(i).texture.region = Rect2(digit_coords[int(s[i])], Vector2(8, 8)) まず、画像内で各数字が検出された座標をリスト化します。次に、display_digits()関数で数値を8桁形式に整形します（例：258 は \"00000258\" に変換）。その後、配列から対応する座標を取得し、各数字の表示位置に適用します。\nユーザーインターフェースのスクリプト化 「UI」シーンに戻り、「ScoreCounter」をHBoxContainerに追加し、その後「UI」にスクリプトを追加してください。\nextends MarginContainer @onready var shield_bar = $HBoxContainer/ShieldBar @onready var score_counter = $HBoxContainer/ScoreCounter func update_score(value): score_counter.display_digits(value) func update_shield(max_value, value): shield_bar.max_value = max_value shield_bar.value = value これらの関数はスコアやシールドを更新する必要がある場合に、Mainモジュールから呼び出されるようにします。\nメイン画面にUIを追加 次にMainシーンに CanvasLayer ノードを追加し、その子要素として UI インスタンスを配置します。CanvasLayer ノードは新しい描画レイヤーを生成するため、UI はゲームの他のコンテンツの上に重なって表示されます。\nメインスクリプト main.gd 内のこの関数を変更します。\nfunc _on_enemy_died(value): score += value $CanvasLayer/UI.update_score(score) ゲームを実行し、敵を撃つとスコアが上がることを確認してください。\nプレイヤーシールド また、シールドをプレイヤースクリプトに追加することもできます。player.gdファイルの先頭にこれらの新しい行を追加してください。\nsignal died signal shield_changed @export var max_shield = 10 var shield = max_shield: set = set_shield この set = 構文は、Godotに対して、shield 変数の値が変更されるたびに set_shield() 関数を呼び出すよう指示するものです。\nfunc set_shield(value): shield = min(max_shield, value) shield_changed.emit(max_shield, shield) if shield \u003c= 0: hide() died.emit() また、艦船のarea_enteredシグナルも接続することで、敵が艦船に命中したことを検出できます。\nfunc _on_area_entered(area): if area.is_in_group(\"enemies\"): area.explode() shield -= max_shield / 2 敵の攻撃弾については、命中時にシールドに追加ダメージを与えるようにしてください。\nfunc _on_area_entered(area): if area.name == \"Player\": queue_free() area.shield -= 1 最後に、プレイヤーノードの shield_changed シグナルをシールドバー表示用UI関数に接続が必要です。メインシーン内の Player ノードを選択してインスペクターから実行できます。［ノード］タブで shield_changedシグナルをダブルクリックすると、「シグナル接続」ウィンドウが開きます。この画面で、対象となる UI ノードを選択し、受信メソッド 欄に update_shield と入力してください。\nゲームを再度実行し、弾丸や敵の攻撃を受けた際にシールドが減少することを確認してください。\n次のステップ 基本機能はほぼ完成しました。あとはゲームの開始と終了方法を追加するだけです。\n戻る 次へ ","description":"","tags":null,"title":"ユーザーインターフェースとスコア","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_09/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 キャラクターまたはリジッドボディを移動させるコンベアベルトオブジェクトを作成したい。\n解決策 constant_linear_velocity プロパティを使用することで、StaticBody2Dだけでコンベアベルトオブジェクトを作成できます。\nメモ この問題を3D環境で解決する方法については以下を参照ください。\n以下に具体例を示します。StaticBody2DとRigidBody2Dを使用しています。追加コードは一切ありません。スタティックボディの線速度一定は (200, 0) に設定されています。\nベルトの動きをアニメーション化 コンベアベルトの「外観」を表現する方法は、使用するアートアセットによって異なります。今回のデモプロジェクトでは、以下の単一サイズ88×88ピクセルのタイルマップのみを使用しています。\nスタティックボディに Sprite を追加し、 テクスチャ セクションで「新規AtlasTexture」を選択してください。\nタイルテクスチャをプロパティのTexture項目に配置し、Regionを(0, 0, 880, 88)に設定してください。\n880を選択すれば、幅が正確に10タイルのコンベアベルトを作成できます。必要な幅を自由に設定できます。\nヒント 画像が繰り返されていない、または表示がおかしい場合、リピート フラグを「有効」に設定して再インポートしてください。\nRegion プロパティの x の値を調整してみてください。タイルがずれるのが確認できるはずです。これがベルトのアニメーションに必要な動きです。このアニメーションは AnimationPlayerを使用するか、コードで実装するかです。ここでは後者の方法を実演します。\nextends StaticBody2D @export var speed = 100 func _ready(): constant_linear_velocity.x = speed func _process(delta): $Sprite.texture.region.position.x -= speed * delta このコードは、ベルトが目標速度で動作することと、アニメーションが物理効果と同期することを保証します。注意点として、方向は反対になっています。領域の x 値を増やすと画像は左方向に移動します。\nこれはキャラクターボディに対しても完璧に動作します。同じコンベアベルトオブジェクトの追加する場合は、プラットフォームキャラクターを参照してください。\n3D 執筆時点では、constant_linear_velocityはStaticBodyを使用した3D環境で正しく動作しません。\nただし、プロジェクト設定で「弾丸」から「GodotPhysics」エンジンに変更すれば、この手法を使用できます。\n関連レシピ プラットフォームキャラクターの実装方法 CharacterBody2Dコンポーネントの活用法 移動式プラットフォームの作成テクニック ","description":"","tags":null,"title":"コンベアベルト","uri":"/godot_recipes/4.x/ja/physics/conveyor_belt/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 3Dゲームで視覚的なデバッグ情報が欲しい。例えば、速度や位置などを表すベクトルを可視化する方法があれば嬉しい。\n解決策 2Dでの描画デバッグは非常に便利です。CanvasItemは_draw()コールバック内で使用できる多彩なプリミティブ描画メソッドを提供します。3Dの場合、状況はそれほど単純ではありません。一つの解決策としてImmediateGeometryを使って手動でメッシュを作成する方法がありますが、これは非常に手間がかかり、迅速なデバッグには不便です。\n適切な解決策としては、引き続きCanvasItemの描画メソッドを使用することです。そのためにはまず、3D空間内の位置を2Dビューポートに投影する必要があります。幸い、Cameraオブジェクトは、そのunproject_position()メソッドを使ってこれを処理してくれます。\nセットアップ方法 表示レイヤーについては、3DシーンにCanvasLayerコンポーネントを追加し、その中にControlを配置してください。さらに、このControlにスクリプトを割り当てる必要があります。\n例：描画制御ノードがプレイヤーノードを参照しており、そのvelocity(速度)ベクトルを描画したいとします。また、Cameraも参照しています。これらの参照方法については後ほど詳しく説明します。\nvar player var camera func _draw(): var color = Color(0, 1, 0) var start = camera.unproject_position(player.global_transform.origin) var end = camera.unproject_position(player.global_transform.origin + player.velocity) node.draw_line(start, end, color, width) node.draw_triangle(end, start.direction_to(end), width*2, color) func draw_triangle(pos, dir, size, color): var a = pos + dir * size var b = pos + dir.rotated(2*PI/3) * size var c = pos + dir.rotated(4*PI/3) * size var points = PoolVector2Array([a, b, c]) draw_polygon(points, PoolColorArray([color])) ベクトルの始点と終点を求めるためにunproject_position()を使用します。draw_triangle()は、見栄えの良い尖った矢印形状を表示するために用意されています。\nゲームオブジェクトからの簡単なアクセス方法 さて、これをより実用的な機能に改良します。ゲームにはデバッグ用ベクトルを描画したいオブジェクトが多数存在するはずです。敵の向き、加速度ベクトル、目的地など、様々な要素が考えられます。どんなオブジェクトでも簡単にデバッグ描画レイヤーに追加できる仕組みが必要となります。\n以下の手順でDebugOverlayを自動読み込みとして追加し、シングルトンとして設定してください。これにより、どのノードからでもアクセス可能になります。このスクリプトに以下の内容を追加してください。\nextends CanvasLayer @onready var draw = $DebugDraw3D func _ready(): if not InputMap.has_action(\"toggle_debug\"): InputMap.add_action(\"toggle_debug\") var ev = InputEventKey.new() ev.scancode = KEY_BACKSLASH InputMap.action_add_event(\"toggle_debug\", ev) func _input(event): if event.is_action_pressed(\"toggle_debug\"): for n in get_children(): n.visible = not n.visible 視認性を切り替える入力アクションを追加するコードを含めておきました。これにより、任意のプロジェクトに簡単に組み込むことができ、インプットマップを変更する必要がありません。これで、DebugOverlay.draw を使って描画レイヤーを参照できるようになりました。\nメモ ここには他のデバッグレイヤーも追加できます。例えば、プロパティをテキスト形式で表示するようなものです。\nまず、表示したいデバッグ値に関する情報をすべて保持するためのカスタムオブジェクトを定義します。\nextends Control class Vector: var object # The node to follow var property # The property to draw var scale # Scale factor var width # Line width var color # Draw color func _init(_object, _property, _scale, _width, _color): object = _object value = _property scale = _scale width = _width color = _color func draw(node, camera): var start = camera.unproject_position(object.global_transform.origin) var end = camera.unproject_position(object.global_transform.origin + object.get(property) * scale) node.draw_line(start, end, color, width) node.draw_triangle(end, start.direction_to(end), width*2, color) var vectors = [] # Array to hold all registered values. このオブジェクトには、表示したい各ベクトルの機能がすべてカプセル化されており、前述した描画コードも含まれています。_process() メソッド内では、これらのベクトルを描画することができ、その際には必ず現在アクティブなカメラを取得する必要があります。\nfunc _process(delta): if not visible: return update() func _draw(): var camera = get_viewport().get_camera() for vector in vectors: vector.draw(self, camera) 最後に、新しいフォロー対象ベクトルを登録する関数を追加できます。\nfunc add_vector(object, property, scale, width, color): vectors.append(Vector.new(object, property, scale, width, color)) これでゲーム内の任意のオブジェクトから、以下の方法でデバッグ用ベクトルを追加できるようになりました。\nDebugOverlay.draw.add_vector(self, \"velocity\", 1, 4, Color(0,1,0, 0.5)) 以下に、レイキャストと操舵方向を表示するAI制御車両の実例をご紹介します。\n関連レシピ UI：デバッグデータの表示 ","description":"","tags":null,"title":"3Dにおけるベクトル描画","uri":"/godot_recipes/4.x/ja/3d/debug_overlay/index.html"},{"content":"最後のステップとして、ゲームにスタートボタンと「ゲームオーバー」状態を追加してください。\nゲーム開始方法 現在ゲームを実行するとすぐに開始されますが、起動用のボタンを追加してください。\nMainをCanvasLayerの子要素として追加し、そこにCenterContainerを追加し、そのレイアウトをフル矩形に設定します。次に、TextureButtonをその子要素として追加してください。このボタンにStartという名前を付け、その通常状態のテクスチャとしてSTART (48 x 8).png画像を設定します。\nスクリプトの上部に参照を追加：\n@onready var start_button = $CanvasLayer/CenterContainer/Start このボタンのpressedテクスチャをMainに接続し、このコードを追加してください。\nfunc _on_start_pressed(): start_button.hide() new_game() new_game()関数はゲームの開始処理を担当するため、_ready()を変更し、敵を生成しないようにしつつ、ボタンが表示されるようにするだけです。\nfunc _ready(): start_button.show() #\tspawn_enemies() 次に、new_game()関数を追加してください。\nfunc new_game(): score = 0 $CanvasLayer/UI.update_score(score) $Player.start() spawn_enemies() 現在は、シーンを実行するとボタンが表示され、クリックするとゲームが開始されるはずです。\nゲーム終了方法 CenterContainerの子として TextureRect を追加し、ノード名を GameOver に設定します。画像には GAME_OVER (72 x 8).png を使用します。この画像はスタートボタンと重なりますが、問題ありません。ゲームでは一度に表示するのは一つだけだからです。\nスクリプトの上部に別の参照を追加：\n@onready var game_over = $CanvasLayer/CenterContainer/GameOver また、_ready() に game_over.hide() を追加してください。\nMain でプレイヤーの died シグナルを接続してください。\nfunc _on_player_died(): get_tree().call_group(\"enemies\", \"queue_free\") game_over.show() await get_tree().create_timer(2).timeout game_over.hide() start_button.show() この操作で2秒間「ゲームオーバー」画面が表示され、その後スタートボタンに戻るので、再度プレイできます。ぜひお試しいただき、何ゲームか続けて遊んでみてください。\n前へ 次へ ","description":"","tags":null,"title":"ゲームの開始と終了方法","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_10/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 ゲーム内でデータを処理する方法や、柔軟なデータオブジェクトを作成する方法を探しています。\n解決策 GodotのResourceクラスは、データを格納・操作するための強力なツールです。Godotで扱う最も一般的なオブジェクトの多くはResourceタイプを拡張しています。アニメーション、衝突形状、画像など。リソースは単なるデータ保持だけでなく、そのデータを操作することもできます（Unityの ScriptableObject に似ています。）。\nGodot に標準で用意されているすべての リソースタイプに加え、独自のカスタムリソースを作成してゲーム固有のデータを管理することもできます。これはデータの抽象化とカプセル化を実現する利点があり、ゲーム内の他のあらゆるオブジェクトから利用できる汎用的なコンポーネントを作成できます。\n例：プレイヤーの移動 この例では、プラットフォーマーゲームにおけるプレイヤーのHP管理を取り上げます。多くのゲームプレイシステムはプレイヤーのHP・状態と連動しています。\nプレイヤーが障害物に衝突するとダメージを受ける場合があります 敵キャラクターがプレイヤーに触れたり攻撃したりすることでダメージを与えられます オブジェクトを拾う、または特定の場所に立つことでプレイヤーが回復できます ゲーム画面にはHPゲージを表示し、発生する変化を適切に表示する必要があります さらに、他の相互作用も存在する可能性があります。プレイヤーがHPを減らすとゲームのサウンドトラックが変化する、あるいは敵の行動パターンがプレイヤーのステータスに応じて変化する、といった具合です。\nメモ これは意図的に簡略化した例です。実際の運用では、ここで使用している機能よりも多くの機能が必要になる場合や、ゲームのアーキテクチャに合わせてこの例を修正する必要があるでしょう。\nまず最初に、新しいカスタムリソースPlayerHealthを定義する必要があります。このリソースはHPに関連するプロパティを管理する必要があります。さらに、HPの変化（回復やダメージを受けるなど）を処理するための機能とシグナルを提供します。\nScript タブで、 ファイル \u003e新規スクリプト を選択します。Resourceを継承するように設定し、ファイル名を PlayerHealth.gd とします。\nこれを部分ごとに分解して考えてください。\n上部には extends 行とリソースに割り当てる class_name があります。このページ名はエディター内の様々な場所で表示されます。\nextends Resource class_name PlayerHealth 次に、ゲームオブジェクトがプレイヤーのHP変化を監視するために購読できるシグナルがあります。また、HPがゼロに達するなどの追加イベント用のシグナルも実装できます。\nsignal health_changed 使用するプロパティです。\n@export var max_value: int var current_value = 0 この関数を使用すると、HPを最大値に初期化できます。ゲームの再起動時や新しいレベルを開始する際に実行するとよいでしょう。\nfunc reset(): current_value = max_value この関数はプレイヤーにダメージが与えられた際に毎回呼び出されるべきです。\nfunc take_damage(amount): current_value = max(0, current_value - amount) emit_signal(\"health_changed\", current_value) この関数はプレイヤーの回復が必要な時に随時呼び出されるべきです。\nfunc heal(amount): current_value = min(max_value, current_value + amount) emit_signal(\"health_changed\", current_value) 以下が完全なスクリプトです。\nextends Resource class_name PlayerHealth signal health_empty signal health_changed @export var max_value: int var current_value = 0 func reset(): current_value = max_value func take_damage(amount): current_value = max(0, current_value - amount) emit_signal(\"health_changed\", current_value) func heal(amount): current_value = min(max_value, current_value + amount) emit_signal(\"health_changed\", current_value) 新規リソースの作成 一度PlayerHealthクラスを定義すれば、新しいインスタンスを作成できます。インスペクター上部の「新規リソース作成」ボタンをクリックしてください。\n「新規リソース作成」ダイアログでは、様々な種類のリソースが一覧表示されます。検索機能を使って、作成した PlayerHealth タイプを見つけてください。\nこれで、希望するmax_valueを設定し、新しいリソースを.tresファイルとして保存できます。\nリソースの使用方法 リソースの作成と保存が完了したら、いよいよ使用準備が整います。このシナリオでは、以下のオブジェクトがあります。\nプレイヤー： CharacterBody2D コンポーネントを搭載したキャラクターモデル UI： 体力ゲージを表示する ProgressTexture を含むインターフェースシステム 回復ゾーン： その上を通過する対象を回復する Area2D オブジェクト スパイクタイル： 接触時にダメージを与える TileMap 要素のタイルセット ゲームの全コードを掲載するのではなく、HPリソースに関連する部分だけを取り上げます。\nプレイヤー側では、インスペクター経由でリソースを割り当てるために変数を エクスポート しています。移動処理コードの一部として、プレイヤーがスパイクに接触した際には hurt() 関数を呼び出すようになっています。\n@export var health: Resource func _ready(): health.reset() func hurt(amount): # Called when running into obstacles health.take_damage(amount) 回復ゾーン（Area2Dオブジェクト）は、内部に位置しhealthプロパティを持つすべてのオブジェクトに影響を与えます。\nfunc _physics_process(delta): for body in get_overlapping_bodies(): if \"health\" in body: body.health.heal(heal_rate * delta) 最後に、UIにHPステータスを表示するために、同じHPリソースを接続し、そのhealth_changedシグナルに接続します。\n@export var player_health: Resource func _ready(): if player_health: player_health.health_changed.connect(_on_player_health_changed) func _on_player_health_changed(value): healthbar.value = float(value) / player_health.max_value * 100 動作例をご覧ください。\nメモ プロジェクトファイルをこちらからダウンロードしてください: custom_resources.zip\n関連レシピ プラットフォームキャラクター オブジェクトのHPバー ","description":"","tags":null,"title":"カスタムリソースを使用","uri":"/godot_recipes/4.x/ja/basics/custom_resources/index.html"},{"content":"課題 Godot環境でリグ設定済みのアニメーション3Dキャラクターをインポートし、AnimationTreeを使ってそのアニメーションを設定したところです。次は移動機能を実装が必要です。キャラクターコントローラーが必要です。\n解決策 このレシピでは、既にキャラクターモデルとアニメーションをインポートしており、アニメーションの遷移やブレンド処理用に AnimationTree が適切に設定されていることを前提に進めます。まだ準備ができていない場合は、アセットのインポート方法 および キャラクターアニメーションについて を参照してください。参考までに、ここでは セクション説明 でリンクされているアートパックを使用しています。\n衝突の追加 インポートしたシーンのルートノードとして CharacterBody3D を選択しましたが、衝突形状が欠けているとエラーが出ています。まずはこれを修正します。以下の手順に従ってください。\nCollisionShape3D 子要素を追加します その [プロパティ] で CapsuleShape3D を選択します カプセルのサイズと位置を調整し、キャラクターの全身を覆うようにします。参考までに、使用した数値は以下の通りです。\nインポートしたリグは、足部分が「地面」に位置するように配置されています（つまり身体の中心座標に合わせた設定です）。この方法は後で便利になります。プレイヤーが中央に立つ場合、空中に浮いた状態ではなく、実際に地面に立っている状態で表示されるようになるからです。\nGodot の 3D 操作に慣れている方なら、キャラクターが画面後方の +Z 方向に向けていることにもお気づきでしょう。 ノードを選択し、Y軸回転を 180 度に変更して修正してください。\n入力操作 以下のキー操作を使用しています。forward、back、left、right、jump。お好みで任意のキー/ボタンに割り当ててください。\nカメラ機能 プレイヤーを追従する3Dカメラの実装方法には様々なものがあります。この例では、カメラ用マウントとして SpringArm3D を採用します。\nSpringArm3Dノードはレイキャストを実行した後、その子オブジェクトを衝突点に移動させることで動作します。これをカメラに応用すると、プレイヤーとカメラの間に障害物が一切入らない状態を実現でき、この長さを調整することでズーム機能を実装することもできます。\nルートノードの子として追加し、その後に Camera3D をその子要素として追加してください。\n春季アームのプロパティで、スプリング長を 5、マージンを 0.1、位置を (0, 2.5, 0) に設定します。\nWe don’t want the spring arm to collide with the player’s capsule shape, so in the root CharacterBody3D set the collision layer to 2. Since the spring arm is checking collision layer 1, that will prevent the camera hitting the player’s head.\n衝突レイヤーの整理 プレイヤーオブジェクト、環境要素、敵キャラクターなど、さまざまなゲームオブジェクトに対する衝突レイヤーを、最終的には適切に管理することが必要です。\n移動 これでプレイヤーにスクリプトを追加する準備が整いました。まずは必要な変数から始めてください。\nextends CharacterBody3D class_name Knight @export var speed = 5.0 @export var acceleration = 4.0 @export var jump_speed = 8.0 var gravity = ProjectSettings.get_setting(\"physics/3d/default_gravity\") var jumping = false その後で、アクセスに必要なノードへの参照を示します。\n@onready var spring_arm = $SpringArm3D @onready var model = $Rig @onready var anim_tree = $AnimationTree @onready var anim_state = $AnimationTree.get(\"parameters/playback\") ここではanim_treeリファレンスを使用して、アイドル/歩行/ランニングブレンドスペースのブレンド位置と、ジャンプトリガー条件を設定します。まずAnimationTreeを選択し、これらのプロパティがインスペクターに表示されることを確認してください。\nanim_state はアニメーション状態マシンへの参照で、これを使用して異なるアニメーション間の遷移を呼び出すことができます。設定方法については、キャラクターアニメーション レシピを参照してください。\n移動処理は、プレイヤーの入力を取得してmove_and_slide()関数を呼び出すことで実現されます。\nfunc _physics_process(delta): velocity.y += -gravity * delta get_move_input(delta) move_and_slide() プレイヤーの入力は水平方向の動きにのみ適用すべきです（X軸とZ軸）。重力が作用しているのはY軸であるためです。このため、一時的にvelocity.yをゼロに設定し、必要な入力を適用した後、処理終了後に元の値に戻します。\n注: 入力ベクトルはカメラの回転に基づいて回転されます。つまり、キャラクターはカメラが向いている方向に前進します。\nfunc get_move_input(delta): var vy = velocity.y velocity.y = 0 var input = Input.get_vector(\"left\", \"right\", \"forward\", \"back\") var dir = Vector3(input.x, 0, input.y).rotated(Vector3.UP, spring_arm.rotation.y) velocity = lerp(velocity, dir * speed, acceleration * delta) velocity.y = vy その前に、これは機能をテストする絶好の機会です。地面に大きな StaticBody3D を使ったクイックテストシーンを作成するか、ダンジョンパックアセットを使用してシーン制作を開始してください。\n前進／後退／左移動／右移動ができるようになります（まだアニメーションはありません）。\nカメラ操作 それでは、カメラ機能を動作させます。マウス操作でカメラ制御ができるようにします。感度を調整できる変数を追加してください。\n@export var mouse_sensitivity = 0.0015 その後、マウスの動きを検知し、それに応じてスプリングアームを回転させます。アームをX軸周りに回転させると上下に傾きます（マウスのy軸方向の動きが反映されます）。また、Y軸周りの回転は向きを変えます（マウスのx軸方向の動きが反映されます）。さらに、カメラの傾斜角度が過度にならないように制限を設けます。\nfunc _unhandled_input(event): if event is InputEventMouseMotion: spring_arm.rotation.x -= event.relative.y * mouse_sensitivity spring_arm.rotation_degrees.x = clamp(spring_arm.rotation_degrees.x, -90.0, 30.0) spring_arm.rotation.y -= event.relative.x * mouse_sensitivity 実際に操作してみると、「前進」を押すとキャラクターがカメラの向き方向に移動することを確認できるはずです。\n現在は、キャラクターを回転させて、移動方向を向くようにさせる必要があります。\n回転速度用の変数を追加してください。これにより、新しい方位に瞬時にスナップするのを防ぎます。\n@export var rotation_speed = 12.0 次に、_physics_process() 関数の move_and_slide() 呼び出し後に以下を追加してください。\nif velocity.length() \u003e 1.0: model.rotation.y = lerp_angle(model.rotation.y, spring_arm.rotation.y, rotation_speed * delta) lerp_angle() を使用することで、常に最短方向に新しい角度に回転できるようになります（例えば、359度から1度に回す場合のように、遠回りせずに済みます）。\nIWR（アイドル/ウォーク/ラン） アニメーション 移動と回転が実装できたところで、次はアニメーションの選択に進みます。基本的な考え方は、キャラクターの水平速度（x/z軸方向の動き）を取得し、それを使って作成したIWRブレンドスペース内のブレンド位置を設定することです。\nget_move_input() では、プレイヤーの移動速度を設定しています。その直後にブレンド位置を指定できます。\nvelocity = lerp(velocity, dir * speed, acceleration * delta) var vl = velocity * model.transform.basis anim_tree.set(\"parameters/IWR/blend_position\", Vector2(vl.x, -vl.z) / speed) velocity はグローバル座標系ですが、キャラクターモデルが回転しているため、モデルの basis を使用して velocity をローカル座標系に変換する必要があります。変換後、3Dベクトルをブレンドスペースの2Dベクトルにマッピングし、speed で割ることで -1 から 1 の範囲の値を得ます。また、-z方向は前方ですが、+y方向はブレンドスペースにおける前進アニメーションを表すため、値が一致するよう符号を調整します。\n注意: このパラメーターパスは、AnimationTreeインスペクターを確認することで取得できます。実際にスクリプトウィンドウにドラッグ＆ドロップして入力することもできます。\n攻撃方法 攻撃動作については、まず「attack」という入力アクションを追加してください。このコマンドは左マウスボタンに割り当てています。\nAnimationTree には3つの異なる攻撃が存在するため、それらをリスト化します。\nvar attacks = [ \"1h_slice_diagonal\", \"1h_slice_horizontal\", \"1h_attack_chop\" ] それから、_unhandled_input() 関数内で、アクションが押されたときにリストからランダムなアニメーションを選択するようにします。\nif event.is_action_pressed(\"attack\"): anim_state.travel(attacks.pick_random()) ジャンプ動作 ジャンプモーションは複数のアニメーションが連動するため、やや複雑です。参考までに、状態マシンの設定手順を以下に示します。\nまず、「ジャンプ開始」アニメーションに移行するために jumping = true を設定します。これにより状態機械での遷移がトリガーされます。\nif is_on_floor() and Input.is_action_just_pressed(\"jump\"): velocity.y = jump_speed jumping = true anim_tree.set(\"parameters/conditions/grounded\", false) anim_tree.set(\"parameters/conditions/jumping\", jumping) 次に、地面に接地したタイミングを把握する必要があります。これにより、「Jump_Idle」アニメーションから移行可能になります。これを実現するためには、前フレームと比較することで接地状態を追跡する必要があります。上部に新しい変数を追加してください。\nvar last_floor = true そして最初のif文の後に、このステートメントが続きます。\n# We just hit the floor after being in the air if is_on_floor() and not last_floor: jumping = false anim_tree.set(\"parameters/conditions/grounded\", true) last_floor = is_on_floor() 最終的に、段差から飛び降りた際に「Jump_Idle」に直接移行する仕組みがあります。\n# We're in the air, but we didn't jump if not is_on_floor() and not jumping: anim_state.travel(\"Jump_Idle\") anim_tree.set(\"parameters/conditions/grounded\", false) まとめ 機能する制御可能なキャラクターにチェイスカメラと複数のアニメーションを追加しました。次は何が必要でしょうか？\nセクションの説明 を参照すると、3D作業のさらなる事例や、ダウンロード可能なGodotプロジェクトなどの例を確認できます。\n関連動画 ","description":"","tags":null,"title":"キャラクタコントローラー","uri":"/godot_recipes/4.x/ja/3d/assets/character_controller/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 経路を追従し、障害物を避け、世界を移動する方法について他の判断もできるAI制御オブジェクトがほしい。\n解決策 「ステアリング挙動」とは、この問題を解決するために使用可能な様々なアルゴリズムの総称です。どの手法を採用するかは、ゲームの特性、オブジェクトが存在する世界の種類、そして求める「知性」のレベルによって異なります。\nこの例では、「状況に基づく振舞い(Context Behavior)」と呼ばれる手法を採用します。これは、オブジェクトが移動方法を選択するために十分なワールドの情報を得ることを目的とするものです。本テーマについてさらに詳しく知りたい方は、以下の関連リンクを参照してください。\nアンドリュー・フレイ: 状況に基づく振舞いがどのように共有されるべきか\nジェームズ・キーツ：AIコンテキスト行動管理\nこのデモでは、汎用的な「エージェント」オブジェクトを使用します。実際のゲームであれば、これはレースコースを走る車、ダンジョンを巡回するモンスター、あるいは他の種類のゲーム内エンティティなどに相当するでしょう。エージェントは CharacterBody2D を使用しますが、覚えておいていただきたいのは、このテクニックはどんな種類のオブジェクトにも適用可能だということです。アルゴリズム自体は対象が移動する方向を選択する方法に関するものであり、実際の移動方法は完全に別個の問題です。\nアルゴリズムについて まず、エージェントがすべての方向に放射線状に広がる複数のレイを持っていると仮定します（使用する本数については後で説明します。とりあえずここでは8本を使用してください）\nエージェントのスクリプト内では、エージェントが移動したい方向を追跡するための配列interestを定義します。\nもちろん、全て同じ方向なら動けません。あらゆる方向に均等に移動したいはずです！そこで、特定の方向への優先性があると仮定します。主に前進したい場合を考えます。\nこの場合、interest配列は以下のようになります。\n最も強い願望は前進することですが、左前方や右前方も許容範囲内です。しかし、障害物が現れた場合は？\n次に、好ましくない方向を示す第2の配列dangerを導入します。\nこれら2つの配列を組み合わせることで、danger(危険度)に含まれるinterest(興味度)方向を除去することが可能です。残ったinterest方向を合計すると、障害物から離れる新しい方向ベクトルが得られます。\n要約すると：\nオブジェクトのinterst(関心度)方向を特定する danger(危険度)を含む方向をすべて特定する 危険なinterest(関心度)方向がある場合はすべて除外する 残ったinterest(関心度)方向を合算して新たな進行方向を決定する 興味を見つける方法 これはエージェントの目標内容によって異なります。もしその目的が『プレイヤーを追跡すること』であれば、interestの値はプレイヤー方向へ高くなるべきです。複数のターゲットを設定することも可能で、より近い目標ほど高い興味を持ちますが、障害物によって無効化された場合、スコアが低い他の目標が優先されます。\nこのデモでは、何らかのレースゲームを作っていると仮定します。AI制御の車はサーキットを周回が必要です。そのinterest配列はコースに沿って前方を指すように設定すべきで、そうしないと逆走を開始してしまいます。\nこれを実現する方法は複数ありますが、エージェントが実装の詳細を知る必要がないよう、トラックがどの方向に進むべきかを位置情報に基づいて指示するシステムを構築します。いつでも「どの方向が正しいか」と聞けば、トラックがその指示を伝えてくれます。\nコード例 以下にエージェントのコードを示します。まずはエクスポートした値から始めてください。移動パラメーターと、後で容易に調整できるようにしたいその他の値です。例えば look_ahead は、danger レイが障害物を検出する範囲を指定します。\n※注：num_rays(レイの数)は調整可能な設定となっています。これにより、用途に最適な数値を見つけることができます。以下では、レイの数が多い場合のメリット・デメリットについて詳しく解説します。\nextends CharacterBody2D @export var max_speed = 350 @export var steer_force = 0.1 @export var look_ahead = 100 @export var num_rays = 8 # context array var ray_directions = [] var interest = [] var danger = [] var chosen_dir = Vector2.ZERO var velocity = Vector2.ZERO var acceleration = Vector2.ZERO 次の _ready() 関数では、配列を適切にサイズ調整し、ray_directions 配列に実際のレイベクトルを格納します。これらは num_rays に基づいて円周上に均等に配置されます。最初は回転していない状態なので、Vector2.RIGHT が前方方向となります。\nfunc _ready(): interest.resize(num_rays) danger.resize(num_rays) ray_directions.resize(num_rays) for i in num_rays: var angle = i * 2 * PI / num_rays ray_directions[i] = Vector2.RIGHT.rotated(angle) _physics_process() 関数では、コンテキスト配列を埋め、移動処理を実行します。注目すべき点として、アルゴリズムの各ステップを別々の関数に分割しています。目的の方向を特定したら、可能な限りその方向へ向きを変え（steer_force に基づく）、その後移動を行います。\nfunc _physics_process(delta): set_interest() set_danger() choose_direction() var desired_velocity = chosen_dir.rotated(rotation) * max_speed velocity = velocity.linear_interpolate(desired_velocity, steer_force) rotation = velocity.angle() move_and_collide(velocity * delta) それでは、アルゴリズムを構成する3つの主要な機能について説明します。まず、interest配列の設定についてです。前述の通り、世界（この場合はowner）に対して、どちらの方向に移動すべきかを指示するよう求めます。\n各レイの方向に対して、与えられた経路方向との 内積 を計算します。二つの平行なベクトルの内積は 1、直交するベクトルの場合は 0 となることを思い出してください。負の値は無視します - 値が 0 の場合は、その方向へ進む必要がないことを意味します。\n例えば、使用するレイが32本の場合、interestは以下のようになります。\n安全対策として、案内してくれる所有者がいない場合は、自動的に前進を試みる設定になっています。\nfunc set_interest(): # Set interest in each slot based on world direction if owner and owner.has_method(\"get_path_direction\"): var path_direction = owner.get_path_direction(position) for i in num_rays: var d = ray_directions[i].rotated(rotation).dot(path_direction) interest[i] = max(0, d) # If no world path, use default interest else: set_default_interest() func set_default_interest(): # Default to moving forward for i in num_rays: var d = ray_directions[i].rotated(rotation).dot(transform.x) interest[i] = max(0, d) 次に、danger配列を埋めます。Physics2DDirectSpaceStateを使用して、各方向にレイをキャストします。衝突があった場合、その位置に1を追加してください。\nfunc set_danger(): # Cast rays to find danger directions var space_state = get_world_2d().direct_space_state for i in num_rays: var result = space_state.intersect_ray(position, position + ray_directions[i].rotated(rotation) * look_ahead, [self]) danger[i] = 1.0 if result else 0.0 最後に、コンテキスト配列を使用して方向を選択できます。danger 配列をループ処理し、危険が存在する場所のすべての interest 値をゼロに設定します。その後、残りの興味ベクトルをすべて加算して正規化します。\nfunc choose_direction(): # Eliminate interest in slots with danger for i in num_rays: if danger[i] \u003e 0.0: interest[i] = 0.0 # Choose direction based on remaining interest chosen_dir = Vector2.ZERO for i in num_rays: chosen_dir += ray_directions[i] * interest[i] chosen_dir = chosen_dir.normalized() 実際の使用例 実際に試してみます！ここでは、Path2Dと衝突判定用のポリゴンを使ってトラックを作成してみました。\nこのシーンのスクリプトには、get_path_direction() 関数が含まれています。位置を指定すると、この関数はパス上で最も近い点を検出し、その位置に PathFollow2D を配置することで進行方向を取得します。\nfunc get_path_direction(pos): var offset = path.curve.get_closest_offset(pos) path_follow.offset = offset return path_follow.transform.x エージェントの移動速度をランダム化してバリエーションを加えました。速いエージェントが低速のエージェントをうまく避けながら進む様子に注目してください。\nまとめ この方法は非常に柔軟性が高く拡張性に優れており、以下のように複雑かつ多様な動作を生成できます。\n以下に、適応策／改善点に関する追加提案を挙げます。\n危険度レベル danger 配列を初期化する際には、単に 0 や 1 を使うのではなく、オブジェクトまでの距離に基づいて「危険スコア」を計算してください。そして、その値を直接削除するのではなく、interest から減算します。遠方にあるオブジェクトは影響が小さく、近くにあるものほど影響力が強くなります。\n回避策 危険な要素が関心を打ち消すのではなく、逆方向への関心を高めることも考えられます。\n関連レシピ ベクトル：内積と外積の活用 この動画が気に入ったら？ ","description":"","tags":null,"title":"状況に基づく操縦","uri":"/godot_recipes/4.x/ja/ai/context_map/index.html"},{"content":"課題 ゲーム要素としてペットやミニオンを追加してください。なので、キャラクターに追従させる必要があります。\n解決策 まず、キャラクターに Marker2D を追加してください。このマーカーは、ペットがプレイヤーの近くに「滞在したい」場所を示すものです。\nこの例では、Sprite2Dの子要素として設定しています。これはキャラクターのコード内で $Sprite2D.scale.x = -1 を使用して左移動時に水平方向を反転させているためです。マーカーはスプライトの子要素であるため、同様に反転表示されます。\nペット用スクリプト 以下はペット用の台本です。\nextends CharacterBody2D @export var parent : CharacterBody2D var speed = 25 @onready var follow_point = parent.get_node(\"Sprite2D/FollowPoint\") parent変数には、ペットが追従すべきキャラクターへの参照情報が格納されています。その後、そのノードからFollowPointを取得し、_physics_process()関数内でその位置情報を取得します。\nfunc _physics_process(delta): var target = follow_point.global_position velocity = Vector2.ZERO if position.distance_to(target) \u003e 5: velocity = position.direction_to(target) * speed if velocity.x != 0: $Sprite2D.scale.x = sign(velocity.x) if velocity.length() \u003e 0: $AnimationPlayer.play(\"run\") else: $AnimationPlayer.play(\"idle\") move_and_slide() 目標地点に近い場合は、ペットの移動を停止します。\n障害物の回避方法 ワールドによっては、ペットが障害物に引っかかってしまう場合があります。より堅牢な追従機能が必要な場合は、ナビゲーションシステムをご利用ください。具体的な実装例についてはタイルマップナビゲーションを参照してください。\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/ai_behavior_demos\n","description":"","tags":null,"title":"追いかけるペット","uri":"/godot_recipes/4.x/ja/ai/pet_following/index.html"},{"content":"課題 複数のオブジェクトを同時に画面上に表示し続けるために、移動・ズーム機能を備えたダイナミックなカメラシステムを作りたいです。\n2人用ゲームで両プレイヤーを画面内に表示したまま、互いに近づき離れていく様子を表現する場合などです。\n解決策 シングルプレイヤーモードでは、カメラをプレイヤーに追従させるのが一般的な手法です。しかし、ここでは2人以上のプレイヤーやその他の重要オブジェクトが常に画面に表示され続ける必要があるため、同様の方法は適用できません。\n以下の3つの機能をカメラに実装が必要です。\nターゲットを任意の数だけ追加/削除可能 カメラの位置をターゲット群の中心点に固定表示 すべてのターゲットが画面内に収まるようズームレベルを調整 以下の手順で新規シーンを作成し、Camera2Dコンポーネントを追加してスクリプトを割り当てます。作業が完了したら、このカメラをゲームに統合します。\nスクリプトの動作原理を分解して説明します。\nメモ スクリプトの全文は記事の末尾で確認できます。\n以下にスクリプトの開始部分を示します。\nextends Camera2D @export var move_speed = 30 # camera position lerp speed @export var zoom_speed = 3.0 # camera zoom lerp speed @export var min_zoom = 5.0 # camera won't zoom closer than this @export var max_zoom = 0.5 # camera won't zoom farther than this @export var margin = Vector2(400, 200) # include some buffer area around targets var targets = [] # Array of targets to be tracked. @onready var screen_size = get_viewport_rect().size これらの設定によりカメラ動作を調整できるようになります。すべてのカメラ変更を lerp() で補間処理するため、移動速度やズーム速度を低く設定した場合、突然の変化に対してカメラが「追いつく」まで若干の遅延が生じます。\n最大・最小ズーム値は、ゲーム中のオブジェクトの大きさや、どこまで接近または遠ざけたいかによっても変わります。適宜調整してください。\nプロパティmarginを使用すると、ターゲット要素の周囲に余分なスペースが追加され、可視領域の端にぴったりと配置されないようになります。\n最後に、ターゲット配列を取得し、ビューポートサイズを確認することで、適切なスケール計算が可能になります。\nfunc add_target(t): if not t in targets: targets.append(t) func remove_target(t): if t in targets: targets.erase(t) ターゲットの追加/削除については、2つの補助関数を用意しています。ゲームプレイ中にこれらを使用することで、追跡対象を変更できます（「プレイヤー3がゲームに参加しました！」）。また、同じターゲットを複数同時に追跡することは避けたいため、既に存在する場合は追加を拒否する仕組みになっています。\n機能の大部分は _process() メソッドで実装されています。まず、カメラの移動処理から見ていきます。\nfunc _process(delta): if !targets: return # Keep the camera centered between the targets var p = Vector2.ZERO for target in targets: p += target.position p /= targets.size() position = lerp(position, p, move_speed * delta) ここでは、ターゲットの位置を繰り返し処理し、共通の中心点を見つけます。lerp()関数を使うことで、スムーズに移動させることができます。\n次に、ズーム機能について説明します。\n# Find the zoom that will contain all targets var r = Rect2(position, Vector2.ONE) for target in targets: r = r.expand(target.position) r = r.grow_individual(margin.x, margin.y, margin.x, margin.y) var z if r.size.x \u003e r.size.y * screen_size.aspect(): z = 1 / clamp(r.size.x / screen_size.x, min_zoom, max_zoom) else: z = 1 / clamp(r.size.y / screen_size.y, min_zoom, max_zoom) zoom = lerp(zoom, Vector2.ONE * z, zoom_speed) ここでの主要な機能はRect2によるものです。すべてのターゲットを包含する長方形を見つける必要があり、これはexpand()メソッドを使用することで取得できます。その後、この矩形にmargin分の大きさを追加してください。\n以下の画面では、長方形が描画されている様子を確認できます（デモプロジェクトで「Tab」キーを押すとこの描画機能を有効にできます）：\n次に、矩形が画面のアスペクト比に対して横長か縦長かに応じて適切なスケーリング係数を求め、事前に定義した最大値/最小値の範囲内で正規化します。\n全スクリプト extends Camera2D @export var move_speed = 30 # camera position lerp speed @export var zoom_speed = 3.0 # camera zoom lerp speed @export var min_zoom = 5.0 # camera won't zoom closer than this @export var max_zoom = 0.5 # camera won't zoom farther than this @export var margin = Vector2(400, 200) # include some buffer area around targets var targets = [] @onready var screen_size = get_viewport_rect().size func _process(delta): if !targets: return # Keep the camera centered among all targets var p = Vector2.ZERO for target in targets: p += target.position p /= targets.size() position = lerp(position, p, move_speed * delta) # Find the zoom that will contain all targets var r = Rect2(position, Vector2.ONE) for target in targets: r = r.expand(target.position) r = r.grow_individual(margin.x, margin.y, margin.x, margin.y) var z if r.size.x \u003e r.size.y * screen_size.aspect(): z = 1 / clamp(r.size.x / screen_size.x, max_zoom, min_zoom) else: z = 1 / clamp(r.size.y / screen_size.y, max_zoom, min_zoom) zoom = lerp(zoom, Vector2.ONE * z, zoom_speed * delta) # For debug get_parent().draw_cam_rect(r) func add_target(t): if not t in targets: targets.append(t) func remove_target(t): if t in targets: targets.remove(t) プロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/multitarget_camera\n","description":"","tags":null,"title":"マルチターゲットカメラ","uri":"/godot_recipes/4.x/ja/2d/multi_target_camera/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n広告について 無料プレイ型モバイルゲームを開発する際、収益化手段としてアプリ内課金と広告の2つの選択肢があります。本記事では、モバイル広告プラットフォーム（AdMob）をゲームに統合する方法について解説します。\n広告は必ずしも好まれるものではなく、その導入の有無は個々のゲーム開発者が判断すべき事項です。本チュートリアルではメリット・デメリットの検討には踏み込みません。ここでは「もし広告を実装したい」と考えている方向けに、その方法を丁寧に解説します。\nAdMobの設定する AdMobに移動してアカウントを作成してください。\nアドモブマネージャーで新しいアプリを作成します - アプリは「サークルジャンプ」というタイトルです - そして「Android」プラットフォームを指定します（iOSについては後ほど説明します）。\n「Circle Jump」アプリでは、「広告ユニット」を3種類作成が必要です。これらはゲーム内で表示可能な各種広告形式です。今回のチュートリアルでは、「バナー広告」と「インタースティシャル広告」が必要になります。各広告ユニットには「Ad Unit ID」という文字列が割り当てられます（これは後でゲーム内で使用します）。\nGodotモジュールの使用について Godot はデフォルトでは広告サービスをサポートしていないため、この機能を追加するにはエンジンモジュールまたはプラグインを使用が必要です。使用するモジュールはこちらから確認できます。 godot-admob。 このページには、プラグインが提供するメソッドの一覧が記載されています。\nカスタムエンジンモジュールを使用するには、エンジンの再コンパイルが必要です。モバイルプラットフォームの場合、デフォルトでダウンロードしたエクスポートテンプレートは本モジュールに対応した形でコンパイルされていないため、それらも再コンパイルが必要です。\nエクスポートテンプレートのコンパイルは難しくありませんが、コンピュータ上にビルド環境を設定する必要があります。これには、Godotをビルドするために必要なプログラムやライブラリをダウンロードすることが含まれます。この概念に慣れておらず詳しく知りたい場合は、公式ドキュメントの [コンパイル] セクションを参照してください Compiling。\n幸いなことに、カスタムエクスポートテンプレートの作成は既に完了しています。godot-custom-mobile-templates GitHubリポジトリに移動してください。「リリース」タブを開き、自分の使用しているGodotバージョンに対応したエクスポートテンプレートをダウンロードします。\n警告 エクスポートテンプレートのバージョンは必ずGodotエディタのバージョンと一致している必要があります。カスタムビルド版のエディタを使用している場合は、同じコードブランチからテンプレートも構築が必要です。\nテンプレートをコンピュータの任意の場所に解凍してください（Circle Jumpプロジェクトフォルダには入れないでください）。\nエクスポートの設定 Godot エディターに戻って、エクスポート設定に変更を加える必要があります。まず、プロジェクト -\u003e プロジェクト設定 を開き、「Android」セクションを見つけてください。モジュール プロパティには、コードで使用するモジュールをリストします。モジュール名は godot-admob ページに記載されています。“org/godotengine/godot/GodotAdMob”。使用するモジュールが複数ある場合は、カンマで区切ってください。\n_プロジェクト設定 -\u003e エクスポートメニューでは、ダウンロードしたカスタムテンプレートを使用するようにGodotに指示が必要です。これらは［カスタムパッケージ］セクションで設定します。フォルダアイコンをクリックして、テンプレートを解凍したディレクトリに移動してください。「デバッグ」と「リリース」両方のテンプレートを必ず追加してください。\nコード例 現在ゲームを実行すると（Android端末上で）、指定したモジュールが読み込まれるようになります。これはエンジンのシングルトン経由でアクセスできます。settings.gdファイルを開いて、以下の内容を追加してください。\nvar admob = null var real_ads = false var banner_top = false # Fill these from your AdMob account: var ad_banner_id = \"\" var ad_interstitial_id = \"\" var enable_ads = true これはモジュール用の設定変数です。real_ads を false に設定すると「テスト広告」モードになります。ゲームリリース準備が整うまでは、これを true に変更しないでください。banner_top は、バナー広告を画面上部に表示するか下部に表示するかを切り替えるトグルです。\nad_banner_id および ad_interstitial_id には、AdMobアカウントから取得した広告ユニット値を設定が必要です。\nモジュールの初期化が必要です。\nfunc _ready(): if Engine.has_singleton(\"AdMob\"): admob = Engine.get_singleton(\"AdMob\") admob.init(real_ads, get_instance_id()) admob.loadBanner(ad_banner_id, banner_top) admob.loadInterstitial(ad_interstitial_id) まずモジュールのシングルトンが存在するかを確認します。存在が確認できれば、モジュールを初期化し、広告ユニットを読み込むことができます。\nfunc show_ad_banner(): if admob and enable_ads: admob.showBanner() func hide_ad_banner(): if admob: admob.hideBanner() 次に、バナーの表示／非表示を制御する機能について説明します。これはメニュー画面でのみ表示し、実際のゲームプレイ中には表示されないようにします。\nfunc show_ad_interstitial(): if admob and enable_ads: admob.showInterstitial() この関数を使用して、ゲーム終了時にインタースティシャル広告を表示します。\nfunc _on_interstitial_close(): if admob and enable_ads: show_ad_banner() このモジュールは、インタースティシャル広告が閉じた時にコードを実行するためのコールバックを探しています。ゲームの終了時でメニューに戻るため、バナーを再表示します。\nこれから、これらの関数をゲームコードから呼び出す必要があります。Main.gdを開いて、以下を追加してください。\nnew_game()関数内でsettings.hide_ad_banner()を追加してください _on_Jumper_died()関数の最後にsettings.show_ad_interstitial()を追加してください デバイスでゲームを起動すると、テスト広告が表示されるはずです。\n広告を無効化する 多くのゲームでは、アプリ内課金や特定レベル到達などによって広告を非表示にできる機能が提供されています。今回の場合、「設定」画面に追加ボタンを設ける形で実装します。\nまず、enable_adsの値を変更できるように、セッター関数を追加してください。\nvar enable_ads = true setget set_enable_ads また、セッター関数を追加してください。\nfunc set_enable_ads(value): enable_ads = value if enable_ads: show_ad_banner() if !enable_ads: hide_ad_banner() この設定により、ボタンを押した際にバナー追加機能が即座に表示／非表示されます。\nボタンを追加するには、3行目のボタン列が必要になります。BaseScreenシーンを開き、最初のHBoxContainerを複製してください。\n[SettingsScreen]シーンに「Ads」という名前のButtonを中央の行に追加してください。テキストを「広告を無効にする」に設定し、カスタムフォント（サイズ48が適切です）を適用し、さらにカスタムスタイルをすべて「New StyleBoxEmpty」に設定してください。最後に、このボタンを「buttons」グループに追加することを忘れないでください。\nScreens.gdファイル内で、ボタン処理用のmatchステートメントに以下を追加してください。\nmatch button.name: \"Ads\": settings.enable_ads = !settings.enable_ads if settings.enable_ads: button.text = \"Disable Ads\" else: button.text = \"Enable Ads\" デバイスでゲームを実行し、広告の有効化／無効化が行えることを確認してください。\nGitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\n動画で見る ","description":"","tags":null,"title":"モバイル広告","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_11/index.html"},{"content":"課題 衝突検出を Line2D に対して行いたい。\n解決策 ノード設定 以下のノードをシーンに追加し、必要に応じてラインを描いてください。\nLine2D StaticBody2D まだボディに衝突形状を追加しないでください！\nメモ 衝突ではなく線との重複を検出したい場合は、代わりに Area2D を使用することもできます。\n次に、ボディに衝突形状を追加が必要です。以下の2つのオプションがあります。\nその1：SegmentShape2Dの使用 SegmentShape2D は線分衝突形状です。この手法の目的は、直線上の各点ペアに対して個別のセグメント衝突を作成することです。\nextends Line2D func _ready(): for i in points.size() - 1: var new_shape = CollisionShape2D.new() $StaticBody2D.add_child(new_shape) var segment = SegmentShape2D.new() segment.a = points[i] segment.b = points[i + 1] new_shape.shape = segment その2：RectangleShape2Dを使用する場合 SegmentShape2D は幅成分を持たないため、線の衝突判定に厚みが必要な場合には、代わりに矩形の衝突判定を使用することをオススメします。\nextends Line2D func _ready(): for i in points.size() - 1: var new_shape = CollisionShape2D.new() $StaticBody2D.add_child(new_shape) var rect = RectangleShape2D.new() new_shape.position = (points[i] + points[i + 1]) / 2 new_shape.rotation = points[i].direction_to(points[i + 1]).angle() var length = points[i].distance_to(points[i + 1]) rect.extents = Vector2(length / 2, width / 2) new_shape.shape = rect プロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/line2d_collision\n","description":"","tags":null,"title":"Line2D衝突判定","uri":"/godot_recipes/4.x/ja/2d/line_collision/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 アーケードスタイルのカーゲームを作りたい場合、リアルな物理演算よりもシンプルさを重視するでしょう。このレシピでは、転がる球体を使って楽しく操作可能な車を作成する方法をご紹介します。\n解決策 ドライビングゲームを作成する方法は数多くあります。各ゲームに求められるリアリズムのレベルは異なります。軽量なアーケードスタイルのカーモデルを作りたい場合、GodotのVehicleBody3Dノードが提供するすべての機能（サスペンションシステムや個別にモデリングされた車輪など）は必要ないかもしれません。\nその代わり、駆動物理処理は単一の RigidBody3D 球体で処理します。球体自体は視認できない状態にし、車のメッシュをこの球体の位置に配置することで、あたかも球体が車を動かしているかのような視覚効果を実現します。\n上記のプレビュー映像でご覧いただけるように、完成した結果は驚くほど良好です（実際にプレイするのもとても楽しいです！）。引き続きお読みいただければ、必要なコード量も驚くほど少ないことがお分かりいただけるでしょう。\n入力値 制御用に、以下の4つの入力を［インプットマップ］に追加してください。\naccelerate(加速) brake(ブレーキ) steer_left(左折) steer_right(右折) キーボード入力、ゲームパッド、または両方を使用できます。ただし、操作性を重視するなら、アナログスティックの使用をオススメします。\nノード設定 この車は主に 2 つのノードで構成されています。物理演算用の RigidBody3D 球体と、車体を表示するための MeshInstance3D です。以下にシーンレイアウトを示します。\nRigidBody3D (Car) CollisionShape3D (Sphere) CarMesh (Imported model) 以下に、各ノードの動作原理を説明します。「加速」を押すと、RigidBody3D に対してオブジェクト（CarMesh）が向いている方向に力が加わります。一方、旋回入力を入力すると、CarMeshが回転します。ボールが転がる際、この動きに伴って車のメッシュも移動します（ここではボール自体の回転は考慮しません）。\nカーメッシュ 以下が使用する車種の詳細です。\nメモ Kenny氏の『カーキット』でこの車種や他のモデルを入手できます。以下のURLからダウンロードできます。 https://kenney.nl/assets/car-kit。すべてのアセットをダウンロードできます。使いたいものだけを選んでいただいて構いません。このキットには複数のフォーマット形式のモデルが含まれていますが、プロジェクトに必要な全てを使用する必要はありません。Godotでの使用にはGLTF形式を推奨します。\nGLTFモデルを使用する場合、インポート設定で調整する必要はありません。\n以下は「suv」モデルをインポートした際のノードツリーの表示例です。\n注：ホイールと車体はそれぞれ別のメッシュです。これにより、ステアリング時に車輪が回転するなど、視覚的な演出を簡単に追加できます。\nボール CollisionShape3D に球形状を追加してください。ここでは半径を 1 に設定していますが、異なる走行挙動を得るには、ボールのサイズを調整して実験してみてください。\n以下の方法でボディの設定を調整できます。\nAngular Damp: 10 - このプロパティはドライビングのフィーリングに大きな影響を与えます。値が大きいほど、車はより速く停止します。 Gravity Scale: 5 - Godotのデフォルトの重力（9.8）は、特にアクション性の高いゲームを目指す場合、少し浮遊感があるように感じられます。この設定は、ジャンプや坂道など、ワールド内の起伏を扱う場合に特に重要になります。必要であれば、プロジェクト設定でグローバルに設定することも可能です。 Physics Material/Bounce: 0.1 - この値を調整することは非常に楽しいですが、0.5を超える値には注意してください！ デモ用にデバッグ用として、衝突形状に球状メッシュも追加しました。必須機能ではありませんが、ボールが転がる様子を視覚的に確認できるとトラブルシューティング時に便利です。\nRayCast 最後に、RayCast3DノードをCarMeshの子要素として追加してください。ターゲット位置は(0, -1, 0)に設定してください。\nこれを地面検知に使用します。車両が空中にある間はステアリングと加速制御が使えなくなります。また、ゲームのコースが平坦でない場合に、この機能を使って車メッシュを斜面に合わせることもできます。\nこれでコーディングを開始する準備が整いました。\nスクリプト まずスクリプトで使用するノード参照を定義します。\nextends RigidBody3D @onready var car_mesh = $CarMesh @onready var body_mesh = $CarMesh/suv2 @onready var ground_ray = $CarMesh/RayCast3D @onready var right_wheel = $CarMesh/suv2/wheel_frontRight @onready var left_wheel = $CarMesh/suv2/wheel_frontLeft 次に、車両の動作を制御する変数について説明します。各変数の機能についてはコメントを参照してください。\n# Where to place the car mesh relative to the sphere var sphere_offset = Vector3.DOWN # Engine power var acceleration = 35.0 # Turn amount, in degrees var steering = 18.0 # How quickly the car turns var turn_speed = 4.0 # Below this speed, the car doesn't turn var turn_stop_limit = 0.75 # Variables for input values var speed_input = 0 var turn_input = 0 インスペクターからこれらを調整したい場合は、@export を指定できます。\n_physics_process()では、車が向いている方向に基づいてボディに力を加え、さらに車オブジェクトをボールの位置に配置し続けます。\nfunc _physics_process(delta): car_mesh.position = position + sphere_offset if ground_ray.is_colliding(): apply_central_force(-car_mesh.global_transform.basis.z * speed_input) 次の手順は入力値を取得することですが、その前にレイが地面と衝突しているかどうかも確認します。\nfunc _process(delta): if not ground_ray.is_colliding(): return speed_input = Input.get_axis(\"brake\", \"accelerate\") * acceleration turn_input = Input.get_axis(\"steer_right\", \"steer_left\") * deg_to_rad(steering) right_wheel.rotation.y = turn_input left_wheel.rotation.y = turn_input ヒント この時点で実際に操作を試してみます。前進・後退は可能になるはずです（ただしまだステアリング操作はできません）。\n次に、引き続き _process() 関数内で、回転入力に基づいて車のメッシュを回転させます。この処理は slerp()（球面線形補間）を使用して滑らかに行います。\n# rotate car mesh if linear_velocity.length() \u003e turn_stop_limit: var new_basis = car_mesh.global_transform.basis.rotated(car_mesh.global_transform.basis.y, turn_input) car_mesh.global_transform.basis = car_mesh.global_transform.basis.slerp(new_basis, turn_speed * delta) car_mesh.global_transform = car_mesh.global_transform.orthonormalized() 警告 浮動小数点演算の精度限界により、数値変換を繰り返し回転させると、スケールが変動したり、軸方向が不整合となり歪みが生じる可能性があります。定期的に変換を回転させるスクリプトでは、orthonormalized() を使用して誤差が累積する前に補正することが推奨されます。\nこの段階で再び試してみることをオススメします。車の制御や走行が可能になり、ほぼ期待通りに動くようになります。ただし、運転感覚をさらに向上させるために、追加すべき点があります。\n仕上げ作業 1. 斜面との整合性を確保 修正が必要な箇所\n坂で運転操作を試したことがある方ならご存知でしょうが、車のメッシュは全く傾かず、常に水平を保っています。これは不自然に見えるため、CharacterBody3D: 表面と整列 セクションで説明されている方法を使って修正しましょう。\n追加するコードは _process() 内でメッシュを回転させた後に配置してください。\nif ground_ray.is_colliding(): var n = ground_ray.get_collision_normal() var xform = align_with_y(car_mesh.global_transform, n) car_mesh.global_transform = car_mesh.global_transform.interpolate_with(xform, 10.0 * delta) align関数（前回同様にorthonormalized()を使用している点に注目してください）。\nfunc align_with_y(xform, new_y): xform.basis.y = new_y xform.basis.x = -xform.basis.z.cross(new_y) xform.basis = xform.basis.orthonormalized() return xform.orthonormalized() 2. 車輪を回転させる フロントホイールがハンドル操作に応じて動くようにすると、よりリアルに見えます。スクリプトの上部にフロントホイールメッシュに関する参照を追加してください。\n@onready var right_wheel = $CarMesh/suv2/wheel_frontRight @onready var left_wheel = $CarMesh/suv2/wheel_frontLeft 入力を取得した直後に以下を追加してください。\n# rotate wheels for effect right_wheel.rotation.y = rotate_input left_wheel.rotation.y = rotate_input 3. 車体を傾ける このスクリプトでは視覚的な魅力を大幅に向上させます。ターン速度に基づいて車のボディを傾斜させる機能を追加してください。スクリプト上部に変数を追加しましょう。\nvar body_tilt = 35 この数値が小さいほど、傾斜効果が強くなります。SUVモデルの場合、35から40の範囲で最適な結果が得られます。\n次に、車のメッシュを回転させた直後に以下を追加してください（if 文内です）：\n# tilt body for effect var t = -rotate_input * ball.linear_velocity.length() / body_tilt body_mesh.rotation.z = lerp(body_mesh.rotation.z, t, 10 * delta) 違いを観察してください。\nクレジット表記 本デモプロジェクトでは以下のオープンソース／クリエイティブ・コモンズ素材を使用しています。\n車両モデル：Kenney氏制作のカーキット コースデータ：Keith氏によるモジュール式レーシングカートトラック（起伏のある地形テーマ）、Fertile Soil Productions提供 プロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。https://github.com/godotrecipes/3d_car_sphere\n関連レシピ 入力アクション ","description":"","tags":null,"title":"アーケードスタイルのカーゲーム","uri":"/godot_recipes/4.x/ja/3d/3d_sphere_car/index.html"},{"content":"課題 3D空間で飛行機のコントローラーを作りたいが、完全なフライトシミュレーター機能は不要です。\n解決策 このレシピでは、単純な航空機コントローラーを作成します。ここで言う「単純な」とは、基本機能だけに絞り込むことを意味します。目指しているのは、飛行機の操縦感覚――特別な訓練なしですぐに飛び立て、最小限の操作体系で簡単に飛行を楽しめるような体験です。\nメモ このレシピは厳密な飛行シミュレーターではありません。航空力学を再現しているわけではないので、本物の飛行機のように飛ぶわけではありません。ここでは正確性よりもシンプルさと「楽しさ」を追求しています。\nノード設定 このシーンでは CharacterBody3D を使用します。実際の飛行力学（揚力、抗力など）はシミュレートしないため、この場合 RigidBody3D は必要ありません。\n以下にモデルのセットアップをご説明します。\n衝突判定用に円筒を使用し、機体のサイズに合わせて調整しています。これにより、デモで必要となる地面との接触検出が可能になります。\nスクリプトを実行する前に、航空機のプロパティを確認してください。\nextends CharacterBody3D # Can't fly below this speed var min_flight_speed = 12 # Maximum airspeed var max_flight_speed = 40 # Turn rate var turn_speed = 0.75 # Climb/dive rate var pitch_speed = 0.5 # Wings \"autolevel\" speed var level_speed = 3.0 # Throttle change speed var throttle_delta = 50 # Acceleration/deceleration var acceleration = 6.0 # Current speed var forward_speed = 0 # Throttle input speed var target_speed = 0 # Lets us change behavior when grounded var grounded = false var turn_input = 0 var pitch_input = 0 操作方法 このデモではゲームコントローラーを使用していますが、お好みでキーボード入力を追加することもできます。\nこの機能は入力値を取得し、設定された値を反映します。なお、スロットルの増減操作は「actual_speed」ではなくtarget_speedを変更することに注意してください。これにより、現在の速度から目標速度までスムーズに加速・減速が可能になります。\nfunc get_input(delta): # Throttle input if Input.is_action_pressed(\"throttle_up\"): target_speed = min(forward_speed + throttle_delta * delta, max_flight_speed) if Input.is_action_pressed(\"throttle_down\"): var limit = 0 if grounded else min_flight_speed target_speed = max(forward_speed - throttle_delta * delta, limit) # Turn (roll/yaw) input turn_input = Input.get_axis(\"roll_right\", \"roll_left\") # Pitch (climb/dive) input pitch_input = Input.get_axis(\"pitch_down\", \"pitch_up\") 移動 モーション処理は _physics_process() 内で行われ、まず速度を目標値にリニア補間した後、move_and_slide() 関数を使用して移動とスライディングを行います。\nfunc _physics_process(delta): get_input(delta) # Accelerate/decelerate forward_speed = lerpf(forward_speed, target_speed, acceleration * delta) # Movement is always forward velocity = -transform.basis.z * forward_speed move_and_slide() テスト方法：テストシーンにこの平面を追加してください（ Cameraの設定を忘れずに）。\"throttle_up\"入力を押すと、平面が前方へ加速する様子が確認できるはずです。\nヒント 補間カメラ機能 をこのデモで実装しています。\n次に、飛行機のピッチ角度を変更する処理を追加してください。_physics_process() 内の get_input() 呼び出し直後に以下を追加してください。\ntransform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) シーンを再度実行し、上下にパンしてみてください。\nその後、ターン入力用に以下を追加してください。\ntransform.basis = transform.basis.rotated(Vector3.UP, turn_input * turn_speed * delta) 注意してほしいのですが、機体が旋回する際に、その動きがやや不自然に見えます。航空機はターン時に「バンク」（傾き）しますので、メッシュの回転角度を変更することでこれをアニメーション化します。\nmesh.rotation.z = lerpf(mesh.rotation.z, -turn_input, level_speed * delta) mesh は、平面シーン内の MeshInstance3D オブジェクトへの参照です（例では $cartoon_plane）。\nロール量はturn_input値に関連しているため、緩やかなターンでは傾きが少なくなります。直進すると自動的に機体が水平になります。\nこれで完了です！基本的な飛行制御が正常に動作するようになり、快適に操縦できるはずです。各種プロパティを調整して、それらが動きにどう影響するか試してみてください。\n着陸/離陸時 上記の方法は空中移動には適していますが、地上での挙動を考えると不十分です。ここでは、非常に簡易的なアプローチ（「簡易」という表現は非常に基本的な方法を意味します - 実際のゲーム要件によっては、さらに拡張が必要になるでしょう）を用いて着陸シミュレーションを実装します。\nまず、地上走行時と空中飛行時を明確に区別する必要があります。地上では速度を0に減速できますが、空中では最低限の対気速度を維持しなければなりません。また、地上走行中は旋回時にバンク（傾斜）しないようにしてください（翼が地面に接触して損傷する危険があるため）。\nfunc _physics_process(delta): get_input(delta) transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) transform.basis = transform.basis.rotated(Vector3.UP, turn_input * turn_speed * delta) # Bank when turning if grounded: mesh.rotation.z = 0 else: mesh.rotation.z = lerpf(mesh.rotation.z, -turn_input, level_speed * delta) # Accelerate/decelerate forward_speed = lerpf(forward_speed, target_speed, acceleration * delta) # Movement is always forward velocity = -transform.basis.z * forward_speed # Landing if is_on_floor(): if not grounded: rotation.x = 0 grounded = true else: grounded = false move_and_slide() その間、get_input()関数では、減速時と降下時にもgroundedを考慮に入れ、min_flight_speed以上の速度に達している場合にのみ離陸を許可するようにします。\nfunc get_input(delta): # Throttle input if Input.is_action_pressed(\"throttle_up\"): target_speed = min(forward_speed + throttle_delta * delta, max_flight_speed) if Input.is_action_pressed(\"throttle_down\"): var limit = 0 if grounded else min_flight_speed target_speed = max(forward_speed - throttle_delta * delta, limit) # Turn (roll/yaw) input turn_input = Input.get_axis(\"roll_right\", \"roll_left\") if forward_speed \u003c= 0.5: turn_input = 0 # Pitch (climb/dive) input pitch_input = 0 if not grounded: pitch_input -= Input.get_action_strength(\"pitch_down\") if forward_speed \u003e= min_flight_speed: pitch_input += Input.get_action_strength(\"pitch_up\") 全スクリプト 以下が完全なスクリプトです。\nClick to expand… extends CharacterBody3D # Can't fly below this speed var min_flight_speed = 12 # Maximum airspeed var max_flight_speed = 40 # Turn rate var turn_speed = 0.75 # Climb/dive rate var pitch_speed = 0.5 # Wings \"autolevel\" speed var level_speed = 3.0 # Throttle change speed var throttle_delta = 50 # Acceleration/deceleration var acceleration = 6.0 # Current speed var forward_speed = 0 # Throttle input speed var target_speed = 0 # Lets us change behavior when grounded var grounded = false var turn_input = 0 var pitch_input = 0 var tracer_scene = preload(\"res://tracer.tscn\") var can_shoot = true @onready var mesh = $cartoon_plane func _ready(): $cartoon_plane/AnimationPlayer.play(\"prop_spin\") func get_input(delta): # Throttle input if Input.is_action_pressed(\"throttle_up\"): target_speed = min(forward_speed + throttle_delta * delta, max_flight_speed) if Input.is_action_pressed(\"throttle_down\"): var limit = 0 if grounded else min_flight_speed target_speed = max(forward_speed - throttle_delta * delta, limit) # Turn (roll/yaw) input turn_input = Input.get_axis(\"roll_right\", \"roll_left\") if forward_speed \u003c= 0.5: turn_input = 0 # Pitch (climb/dive) input pitch_input = 0 if not grounded: pitch_input -= Input.get_action_strength(\"pitch_down\") if forward_speed \u003e= min_flight_speed: pitch_input += Input.get_action_strength(\"pitch_up\") #\tpitch_input = Input.get_axis(\"pitch_down\", \"pitch_up\") func _physics_process(delta): get_input(delta) transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) transform.basis = transform.basis.rotated(Vector3.UP, turn_input * turn_speed * delta) # Bank when turning if grounded: mesh.rotation.z = 0 else: mesh.rotation.z = lerpf(mesh.rotation.z, -turn_input, level_speed * delta) # Accelerate/decelerate forward_speed = lerpf(forward_speed, target_speed, acceleration * delta) # Movement is always forward velocity = -transform.basis.z * forward_speed # Landing if is_on_floor(): if not grounded: rotation.x = 0 grounded = true else: grounded = false move_and_slide() まとめ このテクニックは、様々なアーケードスタイルの飛行ゲームに応用できます。例えば、マウス操作の場合、InputEventMouseMotion の relativeプロパティを使用してピッチとヨー入力を設定する方法が有効です。\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/3d_airplane_demo\n","description":"","tags":null,"title":"アーケードスタイルの飛行機","uri":"/godot_recipes/4.x/ja/3d/simple_airplane/index.html"},{"content":"課題 「Astroids」のような半リアルな宇宙船を作成するため、RigidBody2Dを使いたい。\n解決策 RigidBody2D を使用する際には少し注意が必要です。Godotの物理エンジンによって制御されるため、直接移動させるのではなく力を加える必要があります。リジッドボディを扱う前に、RigidBody2D APIドキュメントを必ず確認することを強くオススメします。これからこの例を進めていく過程で、このドキュメントを参照しながら進めていきます。\n本例では、以下のノード設定を使用します。\nRigidBody2D (Ship) Sprite2D CollisionShape2D Spriteの向き 必ずスプライトを正しく配置してください。回転していないオブジェクトは +X軸 方向に向くようにしてください（つまり右方向を向いている状態）。スプライトのアートワークが別の方向を向いて描かれている場合は、 Sprite2D（親ボディではなく）を適切に整列させるために回転させてください。\n以下の入力を「インプットマップ」で使用します。\nアクション キー thrust w または ↑ rotate_right d または → rotate_left a または ← RigidBody2Dにスクリプトを追加し、変数を定義します。\nextends RigidBody2D @export var engine_power = 800 @export var spin_power = 10000 var thrust = Vector2.ZERO var rotation_dir = 0 最初の2つの変数は、船の「操縦性」を制御する方法を決定します。engine_powerは加速と最高速度に影響を与えます。spin_powerは船が回転する速さを調整します。\nthrust と rotation_dir は入力操作によって設定されます。次にその方法を確認してください。\nfunc get_input(): thrust = Vector2.ZERO if Input.is_action_pressed(\"thrust\"): thrust = transform.x * engine_power rotation_dir = Input.get_axis(\"rotate_left\", \"rotate_right\") もし\"thrust\"入力が有効になっている場合、thrustベクトルを船の進行方向に設定します。一方、rotation_dirは回転入力に応じて +/-1 に設定されます。\nこれらの値を _physics_process() で適用することで飛行を開始できます。\nfunc _physics_process(_delta): get_input() constant_force = thrust constant_torque = rotation_dir * spin_power 動作はしますが、コントロールが非常に難しいのがお分かりでしょう。回転速度が速すぎて、画面外へ出る前に急激に加速してしまいます。ここで「実際の」宇宙物理の法則から脱却したい点があります。宇宙空間には摩擦がありませんが、弊社開発の『アステロイド』風宇宙船の場合、推力をかけていない時には自然に減速するようになれば、より簡単に操縦できるようになります。この制御には「減衰効果」が有効です。\nRigidBody2Dプロパティ内では、直線／減衰と角速度／減衰の設定があります。これらをそれぞれ**1と2**に設定すると、移動/回転の動きが遅くなり、さらに停止させる効果も生じます。\nこれらの値を自由に調整し、engine_power と rotation_dir との関係を試してみて下さい。\n画面巻き戻し機能 画面の端を越えたときの移動処理は本当に「テレポート」です。船が画面右端から外れると、瞬時に左辺に移動します。ただし、単にpositionを変更しようとした場合、すぐに元に戻ってしまうことに気付くでしょう。これは、物理エンジンも位置制御を行っているためです。\nこの問題の解決策として、リジッドボディの _integrate_forces() コールバックを使用することが有効です。この関数内では、物理エンジンが行っている処理と干渉せずに、オブジェクトの物理特性を安全に更新できます。\nスクリプトの上部に画面サイズを設定してください。\n@onready var screensize = get_viewport_rect().size 次に新しい機能を追加してください。\nfunc _integrate_forces(state): var xform = state.transform xform.origin.x = wrapf(xform.origin.x, 0, screensize.x) xform.origin.y = wrapf(xform.origin.y, 0, screensize.y) state.transform = xform ご覧の通り、_integrate_forces() 関数には state というパラメーターが含まれています。このオブジェクトはボディの PhysicsDirectBodyState2D です。ここには、力、速度、位置など、現在の物理特性がすべて保持されています。\n状態から現在の変換行列を取得し、wrapf() 関数を使用して画面全体を覆うように変更した後、元の状態に復元します。\n以下が実際の表示例です。\n歪み補正機能 _integrate_forces()を使用して、問題なくボディの状態を変更できるもう一つの例を見てみてください。『ワープ』機構を追加するとします。プレイヤーが\"warp\"がアサインされた入力を押すと、船が画面内のランダムな位置に瞬間移動するようにします。\nまず、このために新規変数を追加してください。\nvar teleport_pos = null get_input() でランダムな座標をセットします。\nif Input.is_action_just_pressed(\"warp\"): teleport_pos = Vector2(randf_range(0, screensize.x), randf_range(0, screensize.y)) 最終的に、_integrate_forces() 関数では、teleport_position が設定されている場合、まずその値を適用した後にリセットします。\nif teleport_pos: physics_state.transform.origin = teleport_pos teleport_pos = null プロジェクトのダウンロード プロジェクトのサンプルコードはこちらからダウンロードできます。https://github.com/godotrecipes/asteroids_support\n","description":"","tags":null,"title":"アステロイド風物理演算（RigidBody2Dを使用）","uri":"/godot_recipes/4.x/ja/physics/asteroids_physics/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 クリックして指定した位置に3Dオブジェクトを移動させたい。\n解決策 まず、世界として平面から始めてください。オブジェクトはこの平面上を移動します。\nこのデモで使用するアクターは三角柱メッシュです。\n以下に移動動作のコードを示します。目標地点を指定すると、オブジェクトは向きを変えてその方向へ移動します。\nextends CharacterBody3D @export var speed = 5 @export var gravity = -5 var target = Vector3.ZERO func _physics_process(delta): velocity.y += gravity * delta if target: look_at(target, Vector3.UP) rotation.x = 0 velocity = -transform.basis.z * speed if transform.origin.distance_to(target) \u003c .5: target = Vector3.ZERO velocity = Vector3.ZERO move_and_slide() また、シーンに「マーカー」という名前の MeshInstance3D を追加しました。このオブジェクトは、クリックされた位置を示すために移動します。\nマウス→3D 現在、マウスの位置を3D空間にマッピングする手段が必要となります。スクリーンを3D世界の窓と見立てると、マウスは画面ガラス上に固定されています。3D空間で何かを選択するには、カメラ（視点）から出発し、マウスの位置を通って現実世界へと伸びるレイを投影しなければなりません。\nこれは手動でCamera3Dのproject_rayメソッドを使用して行うことも可能ですが、以下のように、CollisionObject3Dノードがこの処理を自動的に行う特性を活用することもできます。必要なのは、StaticBody3Dグラウンドのinput_eventシグナルに接続することだけです。\nfunc _on_StaticBody_input_event(camera, event, click_position, click_normal, shape_idx): if event is InputEventMouseButton and event.pressed: $Marker.transform.origin = click_position $Player.target = click_position マーカーとプレイヤーのターゲットの位置をクリックされた位置に設定します。\nまとめ この手法を使えば、3Dワールド内の任意のオブジェクトに対するクリックを検出できます。\n","description":"","tags":null,"title":"クリックして移動","uri":"/godot_recipes/4.x/ja/3d/click_to_move/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 ダメージを受けたとき、数字を浮かせながら表示(Floating Combat Text)させたい。\n解決策 この問題に取り組む方法は様々です。例えば、ビットマップフォントを使用し、各数字をその構成桁から画像として生成した上で、Sprite2Dノードを使って表示・移動させるといった手法が考えられます。\nただし、このレシピでは Label ノード（名前は「FCT」）を使用します。この方法であれば、フォントの変更が柔軟に行えるだけでなく、数字を文字列として表示するのも簡単になります。さらには状況に応じて「miss」などの他のメッセージを表示することもできます。\nLabelSettings を追加し、お好みのフォントを使用します。“Xolonium.ttf” フォントを使用し、文字サイズを 28 ポイントに、アウトラインの色を黒、幅を 4 ピクセルに設定しています。\nラベルにスクリプトを追加してください。\nextends Label func show_value(value, travel, duration, spread, crit=false): 浮動テキストを出現させる際に、この関数を呼び出してパラメーターを設定します。\nvalue - 表示する数値（または文字列） travel - 移動方向を表す Vector2 オブジェクト duration - テキストが表示される時間の長さ spread - この角度範囲内でランダムに動きが拡散される crit - true の場合、ダメージが「クリティカルヒット」であることを意味するフラグ 以下にこの関数の機能を説明します。\ntext = value var movement = travel.rotated(rand_range(-spread/2, spread/2)) rect_pivot_offset = rect_size / 2 まず、指定された値を設定し、与えられたスプレッド範囲（例：±90度）に基づいて移動をランダム化します。スケーリングもアニメーション化する可能性があるため、制御点の中心からスケールが開始されるよう rect_pivot_offset を中央に設定します。\n$Tween.interpolate_property(self, \"rect_position\", rect_position, rect_position + movement, duration, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) $Tween.interpolate_property(self, \"modulate:a\", 1.0, 0.0, duration, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) 次に、補間する2つのプロパティを設定します。移動用のpositionと、表示制御用のmodulate.aです。\nif crit: modulate = Color(1, 0, 0) $Tween.interpolate_property(self, \"rect_scale\", rect_scale*2, rect_scale, 0.4, Tween.TRANS_BACK, Tween.EASE_IN) クリティカルヒットの場合、色も変更してスケールアニメーションを追加し、より印象的な演出にします。注：ここでは便宜的に赤色をハードコードしていますが、本来は設定可能な値とすべきです。\n$Tween.start() yield($Tween, \"tween_all_completed\") queue_free() 最後に、Tween を開始し、処理が完了するまで待機した後、ラベルを削除します。\nフロートテキストマネージャー 次に、浮遊テキストの表示位置を管理し生成するための小さなノードを作成します。このノードは、浮動テキストエフェクトを表示させたいゲームエンティティにアタッチされます。\nこれは Node2D クラスのノードで「FCTManager」という名前です。このノードには以下のスクリプトが含まれています。\nextends Node2D var FCT = preload(\"res://FCT.tscn\") @export var travel = Vector2(0, -80) @export var duration = 2 @export var spread = PI/2 func show_value(value, crit=false): var fct = FCT.instantiate() add_child(fct) fct.show_value(str(value), travel, duration, spread, crit) 以下の箇所では、設定内容をインスペクターに表示して簡単に変更できます。ここで定義されているshow_value()メソッドは、フローティングラベルを生成し、そのプロパティを設定します。\nゲーム内ユニットでは、このノードのインスタンスを作成して、テキストを表示させたい任意の位置に配置します。その後、ユニットの take_damage() メソッドに以下のようなコードを追加してください。\n$FCTManager.show_value(dmg, crit) まとめ 【最適化】多数の敵や弾が出現する場合、テキスト表示オブジェクトの頻繁な生成と破棄によりパフォーマンスが低下する可能性があります。この場合は、マネージャ内で固定数のテキストオブジェクトを生成し、アニメーション終了時に破棄するのではなく、表示／非表示を切り替える方法が有効です。\nメモ 本デモで使用しているアートワークはLuis Zuno氏による作品です。\nプロジェクトのダウンロード 以下からプロジェクトのサンプルコードをダウンロードできます。https://github.com/godotrecipes/floating_combat_text\n関連レシピ UI: ラベル UI: ユニットHPバー –\u003e ","description":"","tags":null,"title":"ダメージのポップアップ表示","uri":"/godot_recipes/4.x/ja/ui/floating_text/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 モバイルゲームには、タッチ操作対応の2Dカメラが必要です。\n解決策 このレシピでは、複数のタッチ操作に対応した汎用2Dカメラを作成します。\nドラッグで画面移動 2本指でピンチイン/アウトすると拡大縮小できます 設定手順 当社のカメラは内蔵ノードを拡張するため、新しいシーンに Camera2D を追加し、「TouchCamera」と名前を付けてください。保存後、スクリプトをアタッチします。\n以下に必要となる変数を示します。\nextends Camera2D @export var target: NodePath # Optional: export these properties for convenient editing. var target_return_enabled = true var target_return_rate = 0.02 var min_zoom = 0.5 var max_zoom = 2 var zoom_sensitivity = 10 var zoom_speed = 0.05 var events = {} var last_drag_distance = 0 もしtargetが設定されている場合、カメラはその対象物を追いかける、または自動的にその位置に戻ることができます。その他のカメラ動作を制御するプロパティ：\ntarget_return_enabled - これを true に設定すると、ドラッグ後にカメラは自動的にターゲット位置に復帰します。 target_return_rate - カメラがターゲット位置に戻る速度を調整します。 min_zoom / max_zoom - ズームの最小/最大倍率制限値を設定します。 zoom_sensitivity - ピンチ操作によるズーム感度を設定します - これは「ズーム動作」が開始されるために必要なピクセル単位の動きを指定します。 zoom_speed - ズーム操作をより滑らかに実行するためのパラメータです。 以下のプロパティも export することで、インスペクターで調整できるようになります。\nその他の変数はカメラの状態を追跡します。events は辞書型変数で、アクティブなタッチスクリーンイベントを保持し、各イベントはindexをキーとして管理されます。last_drag_distanceは、ピンチジェスチャにおける2つのドラッグイベント間の移動距離を記録します。\n_process()関数内では、ターゲットへの移動処理を行います（ターゲットリターンが有効で、タッチイベントが未発生の場合）。\nfunc _process(delta): if target and target_return_enabled and events.size() == 0: position = lerp(position, get_node(target).position, target_return_rate) これにより、カメラを自由に移動させることができ、タッチを離すと自動的にプレイヤー視点に戻ります。\nこれでジェスチャーの追加準備が整いました。まずは「パン」操作から始めてください。\nパン ※このジェスチャーは、プロジェクト設定の「入力デバイス」→「ポインティング」で「マウスからタッチをエミュレート」を有効化することでパソコンでテストできます。\nマウスイベントやキーボードイベントと同様、タッチイベントもInputEventを継承し、同じ入力優先度に従います。処理には_unhandled_input()関数を使用するため、他のノード（例：Controlノード）が先にイベントを処理できます。\nfunc _unhandled_input(event): if event is InputEventScreenTouch: if event.pressed: events[event.index] = event else: events.erase(event.index) まず、タッチイベント（InputEventScreenTouch）をチェックしています。このイベントをevents辞書に追加してください。イベントのindexプロパティが辞書のキーとして使います。また、このイベントが 押されていない（つまりタッチが終了した場合）場合には削除します。\n次は、タッチ動作の後に発生するドラッグ操作について処理が必要です。\nif event is InputEventScreenDrag: events[event.index] = event if events.size() == 1: position += event.relative.rotated(rotation) * zoom.x ドラッグイベントが発生した場合も同様に辞書に追加してください。なお、この場合は既存の値を更新する形になります - 例えば最初のタッチイベント時点で既に存在していたインデックス0が、今回はドラッグイベントとして再登録されることになります。\n単一のイベントのみがアクティブな場合、これは1本指でのドラッグ操作であるため、カメラの位置をそれに応じて調整できます。ただし、移動量を現在のzoom値に基づいてスケーリングする必要がある点に注意してください。そうしないと、ズームイン時にドラッグの動きが不自然に大きく、ズームアウト時には小さくなってしまいます。同様に、カメラが回転している場合、その回転も相対的な値に適用が必要です。これにより、ドラッグ操作が適切な方向にカメラを動かすようになります。\nモバイルデバイスから直接キャプチャした例をご紹介します。黄色の円がタッチ位置を示しています。\nズーム機能 メモ このジェスチャーはコンピュータではテストできません。2回のタッチ操作が必要ですが、マウスではこの動作をエミュレートできないためです。\n「ピンチ」操作でカメラのズーム機能が作動します。これは2回連続のドラッグイベントを検知した場合に発生します。ドラッグ方向が同じ方向に移動する場合はズームイン、反対方向に移動する場合はズームアウトします。\nif event is InputEventScreenDrag: events[event.index] = event if events.size() == 1: position += event.relative.rotated(rotation) * zoom.x elif events.size() == 2: var drag_distance = events[0].position.distance_to(events[1].position) if abs(drag_distance - last_drag_distance) \u003e zoom_sensitivity: var new_zoom = (1 + zoom_speed) if drag_distance \u003c last_drag_distance else (1 - zoom_speed) new_zoom = clamp(zoom.x * new_zoom, min_zoom, max_zoom) zoom = Vector2.ONE * new_zoom last_drag_distance = drag_distance アクティブなドラッグイベントが2件ある場合の処理を記述 drag_distance は各イベント間の距離を示し、last_drag_distance と比較して増減を確認可能 zoom_speed は倍率係数で、ズームイン時は1.05倍、アウト時は0.95倍に調整する 計算結果が指定された範囲を超えないように制限を施し、新しい zoom 値を設定 最後に次回イベント用に last_drag_distance を更新します。\nまとめ これを底本にして、ニーズに合わせて活用できます。以下に、試せる提案をご紹介します。\nズーム動作を滑らかにするにはlerp()を使いましょう。 ズームは自動的にデフォルトレベルに戻ります。 ダブルタップでリセットできます。 さらにジェスチャーを追加可能(例：3本指操作など) 完全な参考として、以下にTouchCamera.gdの全スクリプトを記載します。\nextends Camera2D @export var target: NodePath var target_return_enabled = true var target_return_rate = 0.02 var min_zoom = 0.5 var max_zoom = 2 var zoom_sensitivity = 10 var zoom_speed = 0.05 var events = {} var last_drag_distance = 0 func _process(delta): if target and target_return_enabled and events.size() == 0: position = lerp(position, get_node(target).position, target_return_rate) func _unhandled_input(event): if event is InputEventScreenTouch: if event.pressed: events[event.index] = event else: events.erase(event.index) if event is InputEventScreenDrag: events[event.index] = event if events.size() == 1: position += event.relative.rotated(rotation) * zoom.x elif events.size() == 2: var drag_distance = events[0].position.distance_to(events[1].position) if abs(drag_distance - last_drag_distance) \u003e zoom_sensitivity: var new_zoom = (1 + zoom_speed) if drag_distance \u003c last_drag_distance else (1 - zoom_speed) new_zoom = clamp(zoom.x * new_zoom, min_zoom, max_zoom) zoom = Vector2.ONE * new_zoom last_drag_distance = drag_distance 関連レシピ 入力操作: Input Actions マウスドラッグでのユニットの選択 この動画が気に入ったら？ ","description":"","tags":null,"title":"タッチスクリーンカメラ","uri":"/godot_recipes/4.x/ja/2d/touchscreen_camera/index.html"},{"content":"課題 「内積」と「外積」の意味を理解したい。\n解決策 このレシピでは、ベクトルの「内積」と「外積」の概念を紹介し、それらがどのように活用できるかを説明します。\n内積 内積は二つのベクトルに対して行われる演算で、スカラー値を返します。これは通常、ベクトルAがベクトルBに投影される様子として視覚化されます。\n以下が内積を計算するための公式です。\n\u003e A ⋅ B = ∥ A ∥ ∥ B ∥ cos θ ここでθは2つのベクトル間の角度、||A||はAの大きさを表します。\nこれは両方のベクトルが正規化されている場合（つまり、その大きさがすべて1である場合）に特に有用です。その場合、公式は以下のように簡略化されます。\n\u003e A ⋅ B = cos θ この結果から、内積が二つのベクトル間の角度と直接関係していることがわかります。cos(0) == 1 かつ cos(180) == -1 であることから、内積の値は二つのベクトルがどれだけ平行しているかを示します。\n以下に、この事実を実際の事例でどのように活用できるかご説明します。\n外積 二つのベクトルの外積は、それらに垂直な第三のベクトルであり、その大きさは各ベクトルの大きさとそれらの間の角度によって決まります。\nもう一度確認しますが、正規化ベクトルを使用している場合、結果は以下のように簡略化されます。これは角度と完全に関連しており、その値は-1から1の範囲で変化します。\nメモ クロス積は両方のベクトルに垂直な値となるため、この計算するには3D空間で作業している必要があります。Godotを含むほとんどの2Dフレームワークでは、Vector2.cross() メソッドは結果の大きさを表すスカラー値を返します。\n実用的な応用例 以下のアニメーションをご覧ください。これは Vector2.dot() と Vector2.cross() の結果が、角度の変化に伴ってどのように変わるかを視覚的に示しています。\nこの例は、これらの手法の2つのよくある応用事例を示しています。赤色ベクトルが対象物の前方方向を表し、緑色ベクトルが他の物体に向かう方向を示す場合。\n内積：結果から、対象物体が前方（＞0）にあるか後方（＜0）にあるかを判断できます。 外積：結果から、対象物体が左側（＞0）にあるか右側（＜0）にあるかを判定できます。 ","description":"","tags":null,"title":"ベクトル：内積と外積の活用","uri":"/godot_recipes/4.x/ja/math/dot_cross_product/index.html"},{"content":"課題 プレイヤーの視界外にあるオブジェクトの位置を表示するためのミニマップまたはレーダー風UI要素が欲しい。\n解決策 以下に、目指している実装例をご紹介します。 プロジェクト設定 この機能を説明するため、まずは自動タイルのレシピを使用した簡素な見下ろし型ゲームと見下ろしキャラクター操作レシピに基づくプレイヤーから始めてください。各コンポーネントの動作詳細については、リンク先のレシピを参照してください。\nメモ 本プロジェクトのアートワークはkenney.nl提供のものを使用しています。以下からダウンロードできます。Minimap アセット.\nメインシーン設定は以下のように構成されています。\nCanvasLayer ノードは、ミニマップ／レーダーなどのUIコンポーネントを保持するために使います。このレシピで作成するこれらの要素を収容するためのものです。\nユーザーインターフェース配置 まず最初に、ミニマップのレイアウトを作成してください。ゲーム内に存在する他のUI要素と連携させるためには、スムーズなリサイズが可能で、コンテナベースのレイアウトに適切に統合できるものでなければなりません。\nまず、MarginContainerを追加してください。次に、その［テーマ設定/定数］をすべて5に設定します。このコントロールは残りのノードを保持し、他の要素に影響を与えないようにする役割を果たします。名前は「ミニマップ」とし、シーンを保存してください。\n次に、このプロジェクトに NinePatchRectノードを追加してください。このノードは TextureRectと似ていますが、角や端を引き伸ばさずにリサイズする点が異なります。アセットフォルダから [テクスチャ] プロパティに panel_woodDetail_blank.png 画像をドラッグ＆ドロップしてください。この画像は 128x128ピクセルのもので、ルート MarginContainerノードを拡大すると、画像が伸びすぎて見栄えが悪くなります。\nNinePatchRectsのプロパティを使用することで、引き伸ばした場合もフレームサイズが一定に保たれます。これらのプロパティは「テクスチャ領域」パネルでグラフィカルに定義できますが、直接数値を入力する方が簡単な場合もあります。パッチ余白 セクションにある4つのプロパティをすべて 64 に設定し、ノード名を “Frame” に変更してください。\nサイズを変更するとどうなるか、次に見てみてください。\n次に、フレームの内側部分をグリッドパターン pattern_blueprintPaper.png で埋めたいと思います。\nただし、フレームのサイズがどうあれ自動でタイル表示されるようにする必要があります。また、グリッドエリアはミニマップマーカーが表示される場所なので、枠線を超えて拡張しないようにしなければなりません。\nMiniMapの子要素（かつFrameの兄弟要素）として、新たに MarginContainer を追加してください。テーマオーバーライド/定数 で4つのマージンプロパティをすべて 20 に設定します。このノードの子要素として TextureRect を追加し、Texture を上記の画像に割り当てます。Stretch Mode は「Tile」に設定してください。このノードには「Grid」という名前を付けます。\nルートノードのサイズを変更して効果を確認してください。\nまずはミニマップのサイズを (200, 200) のままにしておきます。ルートノードの [Size] プロパティは [レイアウト] セクションで確認できます。\nこの時点までに、シーンツリーは以下のようになっているはずです。\nマップマーカー Gridの子要素として、名前を「PlayerMarker」とするSprite2Dノードを追加してください。また、 minimapIcon_arrowA.png テクスチャを適用します。スプライトの Transform/Position プロパティに注意してください。 (0, 0) となっており、これによりGridの左上隅に正確に配置されます。\nもし現在のGrid(グリッド)サイズが (150, 150) であれば（これは［サイズ］プロパティで確認できます）、中心座標は (75, 75) になります。ここにPlayerMarkerの座標を設定しましょう。\n心配しないでください。後で自動化します。\n以下の 2 つの Sprite2Dノードを追加してください: “MobMarker” と “AlertMarker”。テクスチャには minimapIcon_jewelRed.png および minimapIcon_exclamationYellow.png を使いましょう。\nこれらのオブジェクトはゲーム内世界の異なる2種類のアイテムを表します。デフォルトでは表示されないよう、各アイテムの横にある「表示/非表示切り替え」ボタンをクリックしてください。\nマップマーカーのスクリプト設定 ここで重要な判断が求められます。ミニマップに世界オブジェクトを配置する方法は、ゲームの設計方針に大きく依存します。このプロジェクトは非常に簡素なデモ版のため、プロセスはシンプルに保ちます。より大規模なゲームでは、より堅牢なアプローチが必要になる場合があります。\n本デモで使用するゲームオブジェクトは2種類です。ランダムにマップを徘徊する Mob (モブ) と、プレイヤーが持ち上げ可能な Crate (クレート) です。これらのオブジェクトの多くがメインシーン内に散らばっています。それぞれを適切に表示するために、先ほど作成したマップマーカーのいずれかを使いましょう。\nミニマップ上に表示させたい各アイテムを「minimap_objects」というグループに追加してください。各オブジェクトのスクリプトにおいて、minimap_iconプロパティを適切に設定します。\n# In the mob's script: var minimap_icon = \"mob\" # In the crate's script: var minimap_icon = \"alert\" これでMinimapにスクリプトを追加する準備が整いました。まず、メインシーンにミニマップが追加された時にインスペクターで割り当て可能なplayerへの参照と、スケールを調整するzoomプロパティを実装します。必要なノードへのアクセスを簡単にするため、@onready変数も用意しました。\nextends MarginContainer class_name Minimap @export var player: Player @export var zoom = 1.5 @onready var grid = $MarginContainer/Grid @onready var player_marker = $MarginContainer/Grid/PlayerMarker @onready var mob_marker = $MarginContainer/Grid/MobMarker @onready var alert_marker = $MarginContainer/Grid/AlertMarker 次は、Dictonaryを使ってユニットに割り当てた minimap_icon タグを対応するマーカーにマッピングします。\n@onready var icons = { \"mob\": mob_marker, \"alert\": alert_marker } 次に、マップサイズとワールドサイズの比率を計算して保持する変数が必要となります。各オブジェクトにアクティブマーカーを割り当てるため、別のDictonaryを使用します。キーは対象オブジェクト（例：Mob または Crate インスタンス）、値は割り当てられたマーカーになります。\nvar grid_scale var markers = {} _ready()では、プレイヤーのマーカーをグリッドの中心に配置し、スケールファクターを計算します。（注： UIのサイズが動的に変わる場合は、resizedシグナルを接続し、これらの処理をコールバック内で行う必要があります）。\nfunc _ready(): await get_tree().process_frame player_marker.position = grid.size / 2 grid_scale = grid.size / (get_viewport_rect().size * zoom) コンテナ内ノードについて Container ノードが子要素をどのように処理するかの特性上、_ready() の時点では子要素の正確なサイズ値が取得できません。このため、グリッドのサイズを正しく取得するには次のフレームまで待つ必要があります。\nまた、すべてのゲームオブジェクトに対応するマーカーを作成します（「minimap_objects」グループを使用）。これは、対応するマーカーノードを複製し、markersディクショナリを介してオブジェクトと関連付けることで実現されます。\nvar map_objects = get_tree().get_nodes_in_group(\"minimap_objects\") for item in map_objects: var new_marker = icons[item.minimap_icon].duplicate() grid.add_child(new_marker) new_marker.show() markers[item] = new_marker マーカーを作成し、それぞれのオブジェクトにリンクさせた今、_process() 内でその位置を更新できます。もしどの player も割り当てられていない場合は、何も行いません：\nfunc _process(delta): if !player: return もしplayerが存在する場合、まずプレイヤーマーカーを回転させてプレイヤーの向きに合わせます。 PlayerMarker スプライトは x 軸に沿ってではなく上方を向いているため、90度を追加する必要があります。\nplayer_marker.rotation = player.rotation + PI/2 次に、各オブジェクトの位置をプレイヤー座標系で計算し、それを基にマーカーの位置を求めます（制御原点が左上にあるため、オフセットとして grid.size / 2 を考慮することを忘れないようにしてください）。\nfor item in markers: var obj_pos = (item.position - player.position) * grid_scale + grid.size / 2 markers[item].position = obj_pos この問題は、マーカーがグリッドの外側にも配置できてしまう点にあります。\nこの問題を解決するには、obj_pos を計算した後、マーカーの位置を設定する前に、その値をグリッドの矩形範囲にクリップしてください。\nobj_pos = obj_pos.clamp(Vector2.ZERO, grid.size) 以下のオプションから1つ選択できます（clamp()を使用する前に設定してください）。最初の選択肢はマーカーを非表示にする方法です。\nif grid.get_rect().has_point(obj_pos + grid.position): markers[item].show() else: markers[item].hide() 二点目は、視覚的な表現を変更する方法です。この場合、サイズを小さくすることで被写体がより遠くにあることを示唆します。\nif grid.get_rect().has_point(obj_pos + grid.position): markers[item].scale = Vector2(1, 1) else: markers[item].scale = Vector2(0.75, 0.75) オブジェクトの削除方法 モブが倒されたり、木箱が拾われたりすると、マーカー参照が有効でなくなるためゲームがクラッシュしてしまいます。オブジェクトと一緒にマーカーも確実に削除されるようにする必要があります。以下に、簡易的なデモ環境でこれを実装する簡単な方法を紹介します。\n「minimap_objects」グループに追加したすべてのオブジェクトに signal removed を追加してください。このシグナルは、オブジェクトが破棄（または回収）される際に、マップがそのオブジェクトを識別できるように参照と共に発火させます。\nremoved.emit(self) メインスクリプトの_ready()関数内で、以下のシグナルをミニマップに接続してください。\nfunc _ready(): for object in get_tree().get_nodes_in_group(\"minimap_objects\"): object.removed.connect(minimap._on_object_removed) 現在の処理：minimapスクリプトに受信機能を追加し、マーカーを解放して参照を削除します。\nfunc _on_object_removed(object): if object in markers: markers[object].queue_free() markers.erase(object) ズームの調整方法 ここまでお読みいただいた方には、最後にもう一つの機能を追加してください。この「調整可能なズームレベル」を使えば、地図の上にマウスカーソルを置いた状態でホイールを回すことで、表示の拡大・縮小が可能になります。\nまず、zoomプロパティにセッターを追加してください。\n@export var zoom = 1.5: set = set_zoom func set_zoom(value): zoom = clamp(value, 0.5, 5) grid_scale = grid.size / (get_viewport_rect().size * zoom) ノードMiniMapで、インスペクター内のシグナル _gui_input を接続して、スクロールホイールイベントを処理できるようにします。\nfunc _on_gui_input(event): if event is InputEventMouseButton and event.pressed: if event.button_index == MOUSE_BUTTON_WHEEL_UP: zoom += 0.1 if event.button_index == MOUSE_BUTTON_WHEEL_DOWN: zoom -= 0.1 これで完了です。スクロールインとアウトの効果を確認してみます。\nまとめ このレシピはかなり規模が大きいですが、現在取り組んでいるプロジェクトにも柔軟に組み込めるよう配慮しています。\n追加すると役立つかもしれない項目：\n各種ゲームオブジェクトに対応したより多様なマーカータイプを追加 ユニットが生成されるタイミングで新規ユニットを追加する機能（ヒント：ユニット削除時と同様にシグナルを使用） マーカーをクリックするとその詳細情報が表示されるように改良 グリッドの代わりにマップ画像をそのままミニマップ背景として使用可能に プロジェクトのダウンロード 以下からプロジェクトのサンプルコードをダウンロードできます。https://github.com/godotrecipes/minimap\n関連レシピ 見下ろし方式キャラクター移動 –\u003e\n","description":"","tags":null,"title":"ミニマップ／レーダー","uri":"/godot_recipes/4.x/ja/ui/minimap/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 ゲームには「レベル選択」メニューが必要となります。ユーザーがグリッド形式のオプションから選べるようにしてください。\n解決策 上記の例で示したように、プレイヤーが自由に選択できるレベル「ボックス」で構成されたスクロールグリッドを作成します。まず個々のレベルボックスから始めてください。\n1: レベルボックス 以下にノードの設定を示します。\nLevelBox: PanelContainer Label MarginContainer TextureRect TextureRect はロックアイコンの表示用、 Label はレベル番号の表示用に使います。一方が表示されている間は、もう一方は非表示になります。\nご自由にスタイルを設定できます。例としては。\nインスペクターで LevelBox のカスタム最小サイズを必ず設定してください。ここでは例として (110, 110) を使用していますが、実際のレイアウト要件に応じて調整が必要となります。\n次に、スクリプトを追加し、gui_inputシグナルに接続してください。\n@tool extends PanelContainer signal level_selected @export var locked = true: set = set_locked @export var level_num = 1: set = set_level @onready var lock = $MarginContainer/Lock @onready var label = $Label func set_locked(value): locked = value if not is_inside_tree(): await ready lock.visible = value label.visible = not value func set_level(value): level_num = value if not is_inside_tree(): await ready label.text = str(level_num) func _on_gui_input(event): if locked: return if event is InputEventMouseButton and event.pressed: level_selected.emit(level_num) print(\"Clicked level \", level_num) ここで @tool を使用しているのは、インスペクターでプロパティを変更した場合、その変更が即座に反映されるようにするためです。シーンを実行しなくても変化を確認できます。ぜひお試しください。［ロック］ プロパティをクリックして、ロック表示が出たり消えたりするのを確認してください。\n本プロジェクトでは実際のレベルを読み込む必要がないため、print() 文を使用してクリックが検出されているかをテストできます。\n2: グリッド表示 ボックスシーンが完成したら、次に GridContainer を使用して新規シーンを追加してください。その上に任意の数の LevelBox インスタンスを配置し、列数 値を必ず設定してください。以下は6列に設定した例です。\nこの例では、テーマオーバーライド / 定数 / H分離幅とV分離幅 がどちらも10に設定されています。\nこのシーンをLevelGridとして保存します。メニューでは、複数のインスタンスを使用して希望するレベル数を表示します。\n3: メニュー画面 これで最終的なメニューを作成できます。\n以下が基本レイアウトの概要です。\n以下のノードを使用して作成します。\nLevelMenu: MarginContainer VBoxContainer Title: Label HBoxContainer BackButton: TextureButton ClipControl: Control NextButton: TextureButton ノードプロパティを調整：\nLevelMenu テーマオーバーライド/定数/マージン: 20 VBoxContainer テーマオーバーライド/定数/間隔: 50 Title フォントスタイルはお好みでカスタマイズ可能 BackButton / NextButton Ignore Texture Size: 有効化 Stretch Mode: 中央固定 Layout/Container Sizing/Horizontal/Expand: 有効化 ClipControl Layout/Clip Contents: 有効化 Layout/Custom Minimum Size: (710, 350) (レベルグリッドのサイズに相当) ノードClipControl内にグリッドが配置されます。コンテンツを切り取る を有効にすると、コントロール領域を超える内容は自動的に切り取られます。これにより、水平スクロール可能なグリッドセットを作成できるようになります。ClipControlにHBoxContainer 要素をGridBoxという名前で追加し、その内部にインスタンス3個以上の LevelGridを配置してください。\n必ずテーマのオーバーライド／定数／区切り文字を 0 に設定してください。\nレイアウトはこの例と同様に設定してください（動作を分かりやすくするため、コンテンツの切り取り機能は無効にしています）：\n「クリップコンテンツ」を有効にすると、3つのグリッドはすべて表示されますが、ClipControlでは1つずつしか表示されないようになっています。\nさて、メニューをスクロールするには、GridBox を左右に 710 ピクセル分シフトさせる必要があります。\n110 (width of each LevelBox) * 6 (grid columns) + 10 (grid spacing) * 5 == 710 情報 「なぜこの場面でScrollContainerを使わないのか」と疑問に思われるかもしれません。もちろん、使用することはできますが、連続スクロールを望んでいませんし、スクロールバーが表示されることも避けたいからです。\nスクリプトを LevelMenu に追加し、2つのボタンの pressedシグナルを接続してください。\nextends MarginContainer var num_grids = 1 var current_grid = 1 var grid_width = 710 @onready var gridbox = $VBoxContainer/HBoxContainer/ClipControl/GridBox func _ready(): # Number all the level boxes and unlock them # Replace with your game's level/unlocks/etc. # You can also connect the \"level_selected\" signals here num_grids = gridbox.get_child_count() for grid in gridbox.get_children(): for box in grid.get_children(): var num = box.get_position_in_parent() + 1 + 18 * grid.get_position_in_parent() box.level_num = num box.locked = false func _on_BackButton_pressed(): if current_grid \u003e 1: current_grid -= 1 gridbox.rect_position.x += grid_width func _on_NextButton_pressed(): if current_grid \u003c num_grids: current_grid += 1 gridbox.rect_position.x -= grid_width シーンを実行する際は、「次へ」ボタンと「戻る」ボタンをクリックし、期待通りにスクロールされるか確認してください。個々のレベルボックスをクリックするとコンソールに出力が表示されるはずです。\nダウンロード可能なサンプルプロジェクトでは、スクロールアニメーション用のTween機能を含む完全な実装例を確認できます（Tweenを使えば、あらゆる動作がより洗練されたものになります）。\nプロジェクトのダウンロード プロジェクトコードはこちらからダウンロードできます。https://github.com/godotrecipes/ui_level_select\n関連レシピ コンテナ Label を理解する –\u003e –\u003e\n","description":"","tags":null,"title":"レベル選択メニュー","uri":"/godot_recipes/4.x/ja/ui/level_select/index.html"},{"content":"課題 3Dオブジェクトをスムーズに回転させ、新しい方向に向けさせたい場合。\n解決策 この問題に初めて直面した時、おそらくx軸/y軸/z軸に対する角度を表す3つの値である「オイラー角」の概念が頭に浮かぶでしょう。Godotではオブジェクトのオイラー角をrotationプロパティで確認できますが、3D環境で作業する際にこれを直接使用するのは推奨されません。これには理由があります。例えば「ジンバルロック」という問題があり、これは回転のうち1つが90度に達すると、自由度が1つ失われてしまう現象です。\n情報 オイラー角の背景や、ジンバルロックといった関連する問題についてさらに詳しく知りたい方には、こちらの解説動画がオススメです\nGodotではオブジェクトのtransformプロパティを利用することで、3Dオイラー角を使用する必要を回避できます。このプロパティはオブジェクトの空間内における位置と向きを同時に表現します。これは数学的なマトリックス構造によって実現されていますが、実際に使用する際にはその背後にある数学的原理を理解する必要はありません。\nlook_at() 以下の例では、ミサイルや矢のような3Dオブジェクトを目標方向に向ける方法を示します。これはNode3Dクラスのlook_at()メソッドを使用することで実現できます。\nfunc _process(delta): var target_position = $Target.transform.origin $Arrow.look_at(target_position, Vector3.UP) このコードでは、ノード（$Arrow）がターゲットの位置を常に向くようになります。ターゲットがどのように移動しても関係ありません。\n注：look_at() 関数は2つのパラメーターを必要とします。ターゲット位置と「上方向ベクトル」です。飛行機が目標地点に向かって機首を向ける様子を想像してみてください。機体の向きには無数の方法が考えられます。これは、航空機がその軸を中心に回転できるためです。この第2パラメーターによって、最終的な機体の向きをどのように定義するかを指定します。\nスムーズな回転制御 上記のコードは動作しますが、回転を瞬時にターゲット位置にスナップさせます。対象物が非常にゆっくりと動いている場合なら許容できるかもしれませんが、見た目に不自然です。開始姿勢から終了姿勢まで、滑らかに「補間」しながら徐々に回転させる方がより自然に見えるでしょう。\nGodotはこの問題も適切に解決しています。look_at()の代わりに、Transformオブジェクトのlooking_at()メソッドを使用できます。このメソッドはノード自体を回転させることなく、ターゲットを見るための変換行列を返すだけです。さらに、interpolate_with()メソッドと組み合わせることで、現在の向きから目標の向きへと滑らかに遷移させることができます。\nvar speed = 5 func _process(delta): var target_position = $Target.transform.origin var new_transform = $Arrow.transform.looking_at(target_position, Vector3.UP) $Arrow.transform = $Arrow.transform.interpolate_with(new_transform, speed * delta) 注：interpolate_with() は transform を操作するため、回転と位置の両方に対して補間を行えます。\nまとめ これで完了です！この便利な方法を使って3Dオブジェクトを回転させ、角度の計算に煩わされることなく作業を進めてください！\n関連レシピ ","description":"","tags":null,"title":"滑らかな回転","uri":"/godot_recipes/4.x/ja/3d/rotate_interpolate/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n設定の保存について ゲームに追加した3つの切り替え可能プロパティは正常に動作していますが、アプリケーションを終了すると設定が保存されません。次回起動時にも同じ設定値が保持されるよう、これらを設定が必要です。\nまず、設定ファイル res://settings.gd で定義を行います。\nvar settings_file = \"user://settings.save\" 次に、保存したい3つのゲーム設定について、保存／読み込み機能を追加していきます。\nfunc save_settings(): var f = File.new() f.open(settings_file, File.WRITE) f.store_var(enable_sound) f.store_var(enable_music) f.store_var(enable_ads) f.close() func load_settings(): var f = File.new() if f.file_exists(settings_file): f.open(settings_file, File.READ) enable_sound = f.get_var() enable_music = f.get_var() self.enable_ads = f.get_var() f.close() load_settings() を呼び出すタイミングを次のように変更してください。\nset_enable_ads() の終了時に _ready() 内で呼び出す save_settings() の終了時にも同じく _ready() 内で呼び出す さらに、Screens.gd ファイルでは、サウンド/音楽設定が変更された際に状態を保持する必要があるため、match 文内の各ケース部分に settings.save_settings() を追加が必要です。 別の課題として、ゲーム起動時に設定メニューのアイコンが保存ファイルから読み込んだ状態を反映しない問題があります。これはregister_buttons()関数で対処できます。すでにこの関数は全てのボタンをループ処理してシグナル接続を行っています。\nfor button in buttons: button.connect(\"pressed\", self, \"_on_button_pressed\", [button]) match button.name: \"Ads\": if settings.enable_ads: button.text = \"Disable Ads\" else: button.text = \"Enable Ads\" \"Sound\": button.texture_normal = sound_buttons[settings.enable_sound] \"Music\": button.texture_normal = music_buttons[settings.enable_music] スクリーンについて このセクションに追加するもう一つの要素は「ゲームについて」画面です。これはプレイヤーがこのゲームの概要を把握し、ライセンス情報とこのページへのリンクを確認するためのものです。本作はチュートリアルゲームであるため、このような情報提供が不可欠です。\n警告 ライセンス条件を遵守することは非常に重要です。Godotで求められる要件については、以下のページを参照してください。ライセンス遵守について。なお、使用するアートワークについても、クレジット表記やリンク、その他の謝辞が必要となる場合があることにご注意ください。\nこれに到達するために、「タイトル」画面に新規ボタンを追加しました。\nボタンは他のボタンと同様に設定されています。「ボタン」グループに追加して登録されるようにしてください。Screens.gdファイルで、このボタンの名前を対象とするもう一つのmatchを追加してください。\n\"About\": change_screen($AboutScreen) 以下に「バージョン情報」画面の表示内容を示します。\n「BaseScreen.tscn」を継承し、ここにTextEditとホームボタン用のコンテナを追加しました。\nTextEdit設定で［_BBCode］を有効にし、［テキスト］プロパティに以下を入力します。\n[center][u]Circle Jump[/u] [img]res://assets/images/godot_logo.png[/img][/center] Circle Jump is an open source tutorial game made with the Godot Game Engine. You can find the tutorial and the game's source code here: [url=https://github.com/kidscancode/circle_jump]Circle Jump Source[/url] Copyright © 2019 KidsCanCode [url=https://github.com/kidscancode/circle_jump/blob/master/LICENSE]License Information[/url] メモ BBCodeの書式設定に関する詳細な仕様については、RichTextLabelにおけるBBCodeのドキュメントを参照してください。\nURL をクリック可能にするには、TextEdit の meta_clicked シグナルを接続します。\nfunc _on_TextEdit_meta_clicked(meta): OS.shell_open(meta) GitHubでプロジェクトをフォローしてください！ https://github.com/kidscancode/circle_jump\n動画で見る ","description":"","tags":null,"title":"設定を保存しよう","uri":"/godot_recipes/4.x/ja/games/circle_jump/circle_jump_12/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 ご希望の2D弾は弧を描くように移動したり、弾道曲線を描いたりする仕様でしょうか？\n解決策 この問題に対する解決策の一つとして、RigidBody2D コンポーネントを使用することが考えられます。組み込みの物理演算システムにより、発射後も重力によって自動的に地球へ引き戻されるよう設定できます。\nただし、2Dシューティングゲームのレシピで解説されているように、Area2Dは単純な弾丸やその他の投射物に非常に適しています。衝突判定やバウンド、その他の物理挙動を必要としない場合には特に便利です。弾道計算自体は難しくないため、物理エンジンの助けを借りるほどでもありません。\n弾丸の設定 - Bullet (Area2D) - Sprite - CollisionShape2D 以下の方法で利用できます。Area2D の gravity プロパティを設定します。初期テストでは値を 150 に設定します。\nextends Area2D var velocity = Vector2(350, 0) func _process(delta): velocity.y += gravity * delta position += velocity * delta rotation = velocity.angle() func _on_BallisticBullet_body_entered(body): queue_free() ここで必要なのは運動方程式を適用するだけです。velocity の初期値は単なるテスト用です。ブレットシーンを実行してください。\n現在の射撃オブジェクトでは、弾丸インスタンスを作成し、初期プロパティを設定できます。以下のいずれかの射撃処理関数／入力ハンドラに実装してください。\n@export var muzzle_velocity = 350 @export var gravity = 250 func shoot(): var b = Bullet.instantiate() owner.add_child(b) b.transform = $Barrel/Marker2D.global_transform b.velocity = b.transform.x * muzzle_velocity b.gravity = gravity 以下に実際の使用例をご紹介します。\n関連レシピ 2Dシューティングゲームの作成レシピ 2D：軌跡を描画する方法 ","description":"","tags":null,"title":"弾道銃弾","uri":"/godot_recipes/4.x/ja/2d/ballistic_bullet/index.html"},{"content":"課題 オプションを選択するため、「リングコマンド」を実装したい。\n解決策 リングコマンドは様々なゲームで使用されており、複数のボタンを選択するためのインターフェースとして機能します。例えば、ゲーム内のNPCをクリックすると、会話・調査・攻撃など、取るべき行動を選択できるようになります。\nメニューの具体的なデザインは、ゲーム全体の美的感覚と調和させる必要があります。このデモでは、主にメニュー機能の実装に焦点を当て、スタイリングについてはユーザー側で自由に選択できるようにしてください。\n以下にノードの設定を示します。\nルートノードとして TextureButton を使用しています。これがメニューの開閉に使用するボタンです。\nButtons Controlノードは、必要に応じて任意の数のアイテムを配置できるコンテナとして機能します。このコントロールのマウス/フィルタープロパティを “Ignore” に設定することを忘れないでください。これによりマウスクリックが誤って捕捉されるのを防ぎます。\n本例では、クールタイムボタンレシピ から9つのボタンを使用しています。\nそれでは、ボタンのスクリプトを見てみてください。\nextends TextureButton class_name RadialMenuButton @export var radius = 120 @export var speed = 0.25 var num var active = false 以下が変数の定義です。radiusはメニューの「大きさ」を表します、つまり円の半径です。speedはアニメーション用で、数値が小さいほど動作が速くなります。\nnum はボタンの総数を追跡し、active はメニューが開いているか閉じているかを示すフラグとして機能します。\nfunc _ready(): $Buttons.hide() num = $Buttons.get_child_count() for b in $Buttons.get_children(): b.position = position _ready()では、メニューボタンをデフォルトで非表示にし、その位置をメインボタンに設定することから始めます。\nメインボタンのpressedシグナルを接続します。\nfunc _on_pressed(): disabled = true if active: hide_menu() else: show_menu() このボタンをクリックするとメニューの表示／非表示が切り替わります。また、このボタンを一時的に無効化してください。そうしないと、Tween実行中にもう一度クリックすると、Tweenが最初から再生され直してしまいます。\nfunc _on_tween_finished(): disabled = false if not active: $Buttons.hide() Tweenアニメーションが完了したら、アクティブ状態を切り替え、ボタンを再有効化します。\n以下のshow_menu()関数を見てみてください。\nfunc show_menu(): $Buttons.show() var spacing = TAU / num active = true var tw = create_tween().set_parallel() tw.finished.connect(_on_tween_finished) for b in $Buttons.get_children(): # Subtract PI/2 to align the first button to the top var a = spacing * b.get_position_in_parent() - PI / 2 var dest = Vector2(radius, 0).rotated(a) tw.tween_property(b, \"position\", dest, speed).from(Vector2.ZERO).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT) tw.tween_property(b, \"scale\", Vector2.ONE, speed).from(Vector2(0.5, 0.5)).set_trans(Tween.TRANS_LINEAR) この関数では、各アイテム間に必要な角度（spacing）を計算します。その後、ボタンごとにループし、この角度と選択したradiusに基づいて目的地座標（dest）を求めます。それぞれのボタンに対して、2つのプロパティであるpositionとscaleをTween処理することで、希望する効果を実現します。\nhide_menu() はその正反対の機能を果たします。\nfunc hide_menu(): active = false var tw = create_tween().set_parallel() tw.finished.connect(_on_tween_finished) for b in $Buttons.get_children(): tw.tween_property(b, \"position\", Vector2.ZERO, speed).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN) tw.tween_property(b, \"scale\", Vector2(0.5, 0.5), speed).set_trans(Tween.TRANS_LINEAR) 結果、こうなりました。\nプロジェクトのダウンロード プロジェクトのサンプルコードはこちらでダウンロードできます。https://github.com/godotrecipes/ui_radial_menu\n関連レシピ UI：クールタイムボタン ","description":"","tags":null,"title":"リングコマンド","uri":"/godot_recipes/4.x/ja/ui/radial_menu/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\n課題 戦車のような弾道射撃の軌道を描きたい。\n解決策 設定手順 この例では、以下のレシピから「弾道弾丸」を使用します。\n弾道弾の実装方法 そして、以下のように設定されたタンクの配置です。ここでは弾丸が生成される箇所を示すために の Marker2D 要素を使用しています。\nタンクのスクリプトでは、弾丸を以下のようにインスタンス化しています。\nfunc _unhandled_input(event): if event.is_action_released(\"shoot\") and can_shoot: var b = Bullet.instantiate() owner.add_child(b) b.transform = $Barrel/Muzzle.global_transform b.velocity = b.transform.x * muzzle_velocity b.gravity = gravity can_shoot = false このインスタンスでは弾丸オブジェクトを作成し、「World」ノード（戦車のowner）の下に子要素として追加し、初期プロパティを設定します。なお、この例では重力定義に戦車を利用していますが、これは単なるデモンストレーション用です。実際のプロジェクトでは、グローバルな値を使用する方が望ましいでしょう。\n以下に、初期設定の動作例をご紹介します。\nライン設定 メインシーン内（タンクと地面を含む部分）に、Line2D を追加しました。これが軌跡を描画するための要素となります。\nラインの見た目を改善するため、幅を15に設定し、すべての角処理オプションを「丸み」に変更しました。さらに、 Fill(塗りつぶし) セクションにGradient(グラデーション)を追加しました。\n線を引くこと これで線を引く準備が整いました。目標は、投影された軌道に沿って移動しながら、進行状況に応じて線に点を追加していくことです。発射時の初速度と弾丸が使用する重力がわかっているので、同じ計算式を適用できます。\n@onready var tank = $Tank @onready var muzzle = $Tank/Barrel/Muzzle @onready var line = $Line2D var max_points = 250 func update_trajectory(delta): line.clear_points() var pos = muzzle.global_position var vel = muzzle.global_transform.x * tank.muzzle_velocity for i in max_points: line.add_point(pos) vel.y += tank.gravity * delta pos += vel * delta if pos.y \u003e $Ground.position.y - 25: break func _process(delta): if Input.is_action_pressed(\"shoot\"): line.show() update_trajectory(delta) func _on_Bullet_exploded(pos): tank.can_shoot = true line.hide() max_pointsでラインに追加するポイント数の上限を設定します。update_trajectory()関数では、タンクから弾丸の初期位置と速度を取得します（この例では重力もタンク内で定義されています）。その後、これらの点を繰り返し処理し、各「ステップ」でポジションを、1フレーム中に弾丸が移動する量と同じだけ移動させます。\nまた、経路が地面の最上部位置に接した場合には描画を停止するbreakを追加しました。このケースではこれ以上描画を続けないためです。\n最後に、射撃の有無に応じてラインの表示／非表示を切り替えます。\n関連レシピ 2Dシューティングゲームの作成レシピ 2D弾道式弾丸システム ","description":"","tags":null,"title":"軌跡を描画","uri":"/godot_recipes/4.x/ja/2d/2d_draw_trajectory/index.html"},{"content":"課題 キャラクターの体は地面や地形と平行になるように調整が必要となります。\n解決策 このレシピはキャラクターボディ3D：移動制御 レシピで解説されている基本のCharacterBody3Dコントローラーを拡張する内容となっています。まずはそちらを先にお読みください。\nまず、シーンに地形を追加しました。こちらのリンクからダウンロードできます。https://fertile-soil-productions.itch.io/modular-terrain-pack。ローポリモデルですが、お好みの地形を自由に使用・作成していただいて構いません。\nご覧の通り、移動自体は地形に沿って機能していますが、戦車が斜面に対して向きを変えないため、まるで「浮いて」いるかのように見えます。\nその代わりに、戦車の履帯が地面と平行になるように車体を傾ける必要があります。勾配が変わっても姿勢を維持しなければなりません。そのためには、どの方向が「上」かを把握しておく必要があるのです。\n表面法線ベクトル 法線ベクトル（「ノーマルベクトル」または単に「単位ベクトル」）とは、面に垂直な方向を示す単位ベクトルのことです。これは表面がどの向きを向いているのかを定義します。メッシュの場合、各面には必ず外側を指す法線ベクトルが割り当てられます。\nGodot では、衝突が発生した際、接触時の法線ベクトルを取得できます。これは衝突する物体の 接触点における 法線方向になります。\n表面法線を取得した後、タンクのY軸をそれに平行に合わせます。ただし、Transform3D.looking_at() 関数は使用できません。このメソッドでは -Z 軸（前方方向）が法線方向に揃ってしまうためです。\nこれを実現するには、以下の関数を使用します。\nfunc align_with_y(xform, new_y): xform.basis.y = new_y xform.basis.x = -xform.basis.z.cross(new_y) xform.basis = xform.basis.orthonormalized() return xform 与えられた変換行列と新しいY方向ベクトルに基づき、この関数は、指定された法線ベクトルに対してbasis.y成分が一致するよう、適切に回転調整された変換行列を返します。\nメモ クロス積やその他のベクトル数学に慣れていない方のために、Godot公式ドキュメントにベクトル数学入門ガイドが用意されています。\nタンクの移動コードを更新して、表面が衝突した際にこの関数を呼び出せるようにしてください。\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) move_and_slide() for i in get_slide_count(): var c = get_slide_collision(i) global_transform = align_with_y(global_transform, c.get_normal()) この動作は期待通りになりません。\n問題は、タンクの衝突判定形状が地形面のうち複数箇所と干渉している可能性があることです。さらに、move_and_slide() 関数では1フレーム内で複数箇所で衝突が発生する場合があり、これが画面表示のカクつき（ジャダー）を引き起こしています。この問題を解決するためには、単一の面を選択し、一貫してその面で判定する必要があります。\nタンクに RayCast3D 子要素を追加し、 ターゲット位置 を (0, -1, 0) に設定してください。\nこのレイキャストが戦車の中心真下から下方向に発射されているため、衝突する個別の面、すなわち戦車直下の表面に整列させます。\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) move_and_slide(v) var n = $RayCast3D.get_collision_normal() global_transform = align_with_y(global_transform, n) これはかなり改善されましたが、タンクがエッジを越えるたびに瞬時に整列するため、まだ少し不自然に見えます。\nこの最終問題は、即座にスナップするのではなく、新しい変換に補間することで解決できます。\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) velocity = move_and_slide_with_snap(velocity, Vector3.DOWN*2, Vector3.UP, true) var n = $RayCast.get_collision_normal() var xform = align_with_y(global_transform, n) global_transform = global_transform.interpolate_with(xform, 12 * delta) 結果は非常に滑らかでより魅力的なものになります。\n以下の手順でさらに精度の高い結果を得られます。前面と背面にそれぞれレイキャストを1回ずつ実行します。そこから平均的な法線ベクトルを算出します。\nvar n = ($FrontRay.get_collision_normal() + $RearRay.get_collision_normal()) / 2.0 補間量は自由に調整してみてください。この環境で12が最適でしたが、環境によってはさらに高い値や低い値が適している場合もあります。\nプロジェクトのダウンロード プロジェクトのサンプルコードをダウンロードする：https://github.com/godotrecipes/characterbody3d_examples\n関連レシピ CharacterBody3Dの移動 ゲーム数学 補間 ゲーム数学 トランスフォーム ","description":"","tags":null,"title":"CharacterBody3D: 表面に位置合わせ","uri":"/godot_recipes/4.x/ja/3d/3d_align_surface/index.html"},{"content":" ゲーム デモゲームとチュートリアル。\n目次 ： 初めての2Dゲームを作ろう モバイルゲーム：サークルジャンプ ","description":"","tags":null,"title":"ゲームチュートリアル","uri":"/godot_recipes/4.x/ja/games/index.html"},{"content":" \u0026nbsp 3Dカーモデル CharacterBody3Dを使用して3Dカーを作成しています。\n目次 ： 3Dで自動車を作ろう：ベースモデル 3Dで自動車を作ろう：牽引とドリフト 3Dで自動車を作ろう：カメラで追いかけよう 3Dで自動車を作ろう：傾斜面＆スロープ ","description":"","tags":null,"title":"3Dで自動車を作ろう","uri":"/godot_recipes/4.x/ja/3d/kinematic_car/index.html"},{"content":"ここまでついてきてくださった皆さんは、Godotでゲームを構築する基本原理の多くを学べたことでしょう。基本的なゲームが完成しましたので、ここでチュートリアルは終了とします。\n効果的な学習の秘訣 このようなチュートリアルや、オンラインで見つけられる他の教材を最大限に活用するためのとっておきの方法をご紹介します。作業が終わったらすぐにプロジェクトを削除し、最初からやり直してください。今度は、解説を見ずに自分で再現してみることに挑戦してみます。行き詰まった部分だけは解説を参照しても構いませんが、その後は再度閉じて進めてください。\n繰り返しのように聞こえるかもしれませんが、これがまさに学ぶ方法です。何度も実践を重ねることで。ぜひこのアドバイスに従ってみてください。ゲーム開発スキルが驚くほど速く上達するのを実感できるでしょう。\nゲームへの追加方法 このゲームで使われている技術に十分慣れたと感じたら、次は新しいことに挑戦する段階です。ゲームに新しい機能を1つ追加してみます。\nアイデアが浮かばないときは、これらの提案を参考にしてください。\n追加の敵タイプについて - アートパックには他種の敵用アートワークも含まれています。これらの敵はどのように移動し、攻撃するのでしょうか？\nウェーブシステム - 画面をクリアするたびに新たな敵が次々に出現する仕様を追加\nボスキャラクター - 通常の敵より巨大な「ボスキャラ」が登場するようにしたら？\nパワーアップ要素 - プレイヤーが収集できるアイテムとして各種パワーアップを実装。関連するグラフィックも用意済み:\nシールド回復アイテム - これを回収するとシールド効果が強化される 武器アップグレード - 発射弾数増加や新たな攻撃パターン追加など 効果音とBGM - サウンドエフェクトと背景音楽を導入することで、ゲーム全体にさらなる個性と臨場感を与える。\nさらに詳しく学ぶ さらに学習を進めませんか？次の学びの冒険に向けたオススメをご紹介します。\nGodot入門：3D制作の始め方 - 3Dモデリングに興味がある方は、Godotの3D機能に関するこの解説をご覧ください。\nこのウェブサイトの他のコンテンツもご覧ください。夢のゲームを作るための様々な事例、チュートリアル、コードスニペットを豊富にご用意しています。\nこのプロジェクトを GitHubからダウンロード プロジェクトコードはこちらからダウンロードしてください。\nhttps://github.com/godotrecipes/8_direction_animation\n","description":"","tags":null,"title":"まとめ","uri":"/godot_recipes/4.x/ja/games/first_2d/first_2d_end/index.html"},{"content":" Godotレシピ Godotのノードを使って、どんな物を作りますか？\nこのサイトでは、ゲームシステムを構築するための解決策と実例を提供しています。\nGodot 4.0 Godot 4.0が正式リリースされました！\nGodot 4.0はエンジンの最新安定版バージョンです。\nこのサイト(注 : 原文サイトには。この非公式日本語版にはGodot 3の日本語訳はありません。)にはGodot 3向けの学習教材も充実しており、その多くは今でも十分に活用できます。右上のリボンをクリックするとGodot Recipesバージョンを切り替えられますし、以下のボタンからもアクセスできます。\nGodot 3レシピ集 (原文・英語) ゲーム開発を学ぶ準備はできていますか？趣味として、あるいは夢のキャリアへの最初の一歩として、今ほど良いタイミングはありません。現代のプログラミング言語とツールを駆使すれば、高品質なゲームを簡単に作成できます。また世界中に向けて配信できます。その中の一つがGodotゲームエンジンです。初心者でもゲーム開発技術を楽しく、親しみやすい方法で学ぶことができます。一方、経験豊富な開発者にとっては、自らのビジョンを形にするための強力なツールです。カスタマイズも可能で、オープンソースとして利用できます。\nこのサイトでは、Godotゲームエンジンの初心者向けガイドをはじめ、さまざまなゲーム開発のヒントやテクニックを幅広く紹介しています。サイドバーのコンテンツを自由に閲覧して、興味のある分野を見てみてください。\nGodotを初めて使う方はまずは Godotとは？ から始めてください。\nこのサイトの利用方法 初心者の方へ ゲーム開発が初めての場合は、まず はじめてのGodot(基礎編) のセクションから始めてください。ここではGodotアプリケーションの概要を学びます。次に、プロジェクトを段階的に作成していきます。学ぶべきことは多岐にわたりますが、最初はすべてを理解する必要はありません。複雑な概念なので、反復学習が不可欠です。Godotの各種機能を実際に操作すればするほど、その使い方に慣れ、次第に「簡単」だと感じられるようになります。\n情報 このサイトは、プログラミングに関する基本的な経験があることを前提としています。もし完全に未経験で Godot をさわり始める場合は先に、初心者向けの学習ガイドをご覧ください。\n経験豊富な開発者へ 経験豊富な開発者の方や、他のゲームエンジンに精通している方は、左側のメニューから興味ある分野をご覧ください。「Godot流(Godot Way)」の手法で各種タスクを実行する方法を詳細に解説しています。有用なガイドやチュートリアルを多数用意しています。すべての記事について、サンプルコードと実践的なプロジェクト例も提供しています。\n","description":"","tags":null,"title":"ホーム","uri":"/godot_recipes/4.x/ja/index.html"},{"content":"","description":"","tags":null,"title":"Categories","uri":"/godot_recipes/4.x/ja/categories/index.html"},{"content":"Label ラベル は、フォーマットされていないテキストを表示するための コントロールノードであり、テキストの配置や折り返しなどの制御オプションを備えています。\nAPIドキュメント\nノードのプロパティ 完全なリストについてはドキュメントを参照してください。以下では、ノードで最も一般的に使用されるプロパティについて説明します。\ntext - このプロパティはラベルの表示内容を決定します。コード内で変更することで、ラベルに表示させる内容を更新できます。数値を表示する場合は、必ず文字列に変換してください！例えば、指定された数値値でラベルを更新する場合： func update_label(value): $Label.text = str(value) align - このプロパティを使用すると、テキストを右揃え/中央揃え/左揃えにできます。\npercent_visible - このプロパティは表示する文字数を制限します。例えば値を 0.5 に設定するとコンテンツの半分が表示されます。この値をアニメーション化することで「タイプライター効果」を作成できます。\nスクリプトの追加方法 テキスト プロパティに何らかの文字を入力するとすぐに、Godotのデフォルトフォントが要件に対して小さすぎることに気付くでしょう。以下に変更方法を説明します。\nまず、プロジェクトフォルダにTTFまたはOTFフォントファイルが保存されていることを確認してください。\nLabelの「カスタムフォント」セクションにて、“Custom Fonts\"を選択してください。DynamicFontは、指定された書体でテキストを表示するリソース型です。\n追加した\"DynamicFont\"を選択し、“Font/Font Data\"で\"Load\"を選び、フォントファイルを選択します。また、フォントのサイズも設定してください。\n","description":"","tags":null,"title":"Label","uri":"/godot_recipes/4.x/ja/kyn/label/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\nPath2D と PathFollow2D パス とは 2D 空間における点の連続であり、Curve2D リソースによって定義されます。Path2D ノードを使用すると、2D 空間でパスを配置できるほか、エディター内で新規パスを作成することもできます。\nパスには様々な用途があります。敵モブが辿る巡回経路、アニメーション効果のための曲がりくねった道、移動プラットフォームの動作パターンなどを作成できます。\nCurve2Dについて【】 パスのデータはこのリソースオブジェクトに保持されています。これには曲線の数学的表現が含まれており、そのデータと対話するための方法が提供されています。APIドキュメントに詳細が記載されていますが、ここでは特に役立つメソッドをご紹介します。\nadd_point() / remove_point() / clear_points() コード内でパスのポイントを操作する必要がある場合、これらの関数が役立ちます。\nget_closest_point() このメソッドは、空間内の任意の点に最も近い経路上のポイントを返します。\nget_closest_offset() 上記と異なる点として、この方法は指定された地点に最も近い経路上の位置を返します。ただし、この位置は経路上に定義された2つの特定のポイント間に位置する場合もあります。\ntesselate() このメソッドは曲線上の点のリストを返します。これらの点は、曲率が高い経路部分に密集して配置されます。\n経路の描画方法 Path2D ノードを選択すると、アイコンバーに以下の新しいアイコンが表示されます。\nアイコンのいずれかを選択すると、マウスカーソルの動作が変更されます。ホバーするとそれぞれの名前を確認できます。\nポイントを選択 - クリック＆ドラッグで既存のポイントを移動できます。 コントロールポイントを選ぶ - ポイントに調整ハンドルを追加し、カーブ形状を調整できます（詳細は後述）。 ポイントを追加(空きポイントに) - 空白部分をクリックすることで新しいポイントを作成します。 点を削除 - クリックすると指定したポイントを削除します。 曲線を閉じる - 曲線の最終ポイントと最初のポイントを接続してループ状に仕上げます。 「ポイントを追加」を選択し、エディターウィンドウ内でクリックしてポイントを作成します。\nより滑らかで丸みを帯びた曲線を作成するには、「制御点を選択」を選択し、カーブ上の任意の点をドラッグして「内向き」「外向き」ハンドルを調整してください。\n経路の追跡 単体ではPath2Dノードにはほとんど機能がありません。経路に沿って移動するには、PathFollow2D ノードも併せて使用が必要です。これは親の Path2D に沿って移動する役割を持つノードです。\nノードプロパティを調整：\noffset と unit_offset - これらのプロパティは経路上の起点からの距離を表します。offset はピクセル単位で測定され、unit_offset はパーセント値で示されます（例：0 が開始位置、.5 が中間点、1.0 が終点）\nrotate - このブール値により、ノードが経路に沿って移動する際に回転するかどうかを制御します。\nloop - このブール値が true の場合、パス長を超えるオフセットは「巻き戻されます」。繰り返し可能な経路が必要な場合に使いましょう。\n例えば、この平面が経路に沿って移動する様子を考えてみます（経路を可視化するにはメニューから、デバッグ \u003e ナビゲーションを表示 を選択してください。\nこれは、平面ノードを Spriteとして PathFollow2Dの子要素にし、以下の内容を _process() 関数に追加することで実現しています。\nfunc _process(delta): $Path2D/PathFollow2D.offset += 250 * delta offsetを増やすと、ノードが経路に沿って移動します。\n注意: PathFollow2D ノードの 回転 プロパティ により、パスに沿って移動する際、ノード自体とその子ノードが常に経路に沿った方向に向くように維持されます。\n例 経路の探索 以下の例をAI：状況に基づく操縦レシピからご覧ください。\nこの例では、AIエージェントは壁や他のエージェントを回避するだけでなく、正しい進行方向に沿って移動を続けようとします。軌道上には以下の[Path2D]が描画されており、エージェントはこれを参照して現在の進行方向を確認します。\nfunc get_path_direction(pos): var offset = $Path2D.curve.get_closest_offset(pos) $Path2D/PathFollow2D.offset = offset return $Path2D/PathFollow2D.transform.x 任意の経路上の位置において、PathFollow2D の前方方向 (transform.x) は常に経路に沿っています。\n関連レシピ 補間カメラ CharacterBody3D: 移動 –\u003e –\u003e\n","description":"","tags":null,"title":"Path2D と PathFollow2D","uri":"/godot_recipes/4.x/ja/kyn/path2d/index.html"},{"content":" ℹ️ 留意事項 この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。\nRayCast2D レイキャスティング はゲーム開発で広く用いられる手法です。「レイを飛ばす(キャストする)」とは、ある点から直線を伸ばし、それが何かに衝突するか限界に達するまで移動させる操作を指します。\nノードのプロパティ RayCast2D ノードを追加して、インスペクターを確認します。\n以下に主要な特性をご説明します。\nEnabled この機能を無効にするとレイキャスト操作が無効化されます。\nExclude Parent このプロパティにより、レイは親オブジェクトとの衝突を無視するようになります。デフォルトで有効になっています。\nTarget Position これはレイの到達点です。※注：この座標系はローカル座標です。\nまた、「衝突対象」セクションにも注意してください。初期設定ではレイはオブジェクトのみを検出するため、領域も検知したい場合や代わりに使用したい場合は、ここで設定する必要があります。\n便利な機能 ノードの機能一覧はAPIドキュメントで確認できます。特に便利な主要機能をご紹介します。\nis_colliding() ブール型関数。レイが何らかの物体と衝突しているかどうかを判定します。\nget_collision_point() レイが衝突している場合、この関数は衝突位置をグローバル座標系で返します。\nget_collider() 衝突が発生している場合、この関数は衝突中のオブジェクトへの参照を返します。\nget_collision_normal() 別の有用な情報として、衝突点における衝突オブジェクトの法線ベクトルを示します。\n使用例 レイキャストには多様な用途があります。可視判定（AはBを確認できるか、その間に障害物はないか）、近接検出（壁や地面、障害物の近くにいるか）などです。以下に実用的な使用例をご紹介します。\n1. 射撃 高速で移動する発射物には、障害物を「すり抜けてしまう」という問題がよく発生します。これは、衝突判定が1フレーム内で検出できないほど物体の移動速度が速いためです。代替案として、経路（あるいはレーザーなど）を表現するには Raycast2D を使用する方法があります。\nここに、銃の先端にレイキャストが取り付けられたプレイヤースプライトがあります。target_positionは(250, 0)に設定されています。\nプレイヤーが射撃した際、レイが何かに衝突しているかどうかを判定します。\nfunc _input(event): if event.is_action_pressed(\"shoot\"): if $RayCast2D.is_colliding(): print($RayCast2D.get_collider().name) 2. エッジ検出処理 以下の方法でmobに下向きレイキャストを2つ追加してください。\nモブキャラクターのスクリプト内で、レイが衝突を停止したときを確認します。その時点までに境界線に到達したことを意味するので、向きを変える必要があります。\nfunc _physics_process(delta): velocity.y += gravity * delta if not $RayRight.is_colliding(): dir = -1 if not $RayLeft.is_colliding(): dir = 1 velocity.x = dir * speed $AnimatedSprite.flip_h = velocity.x \u003e 0 velocity = move_and_slide(velocity, Vector2.UP) 動作中の様子をご覧ください。\n関連レシピ 補間カメラ CharacterBody3D: 移動 –\u003e –\u003e\n","description":"","tags":null,"title":"RayCast2D","uri":"/godot_recipes/4.x/ja/kyn/raycast2d/index.html"},{"content":"","description":"","tags":null,"title":"Tags","uri":"/godot_recipes/4.x/ja/tags/index.html"}]