Unity 6でスワイプして車を走らせる ― SwipeCarで学ぶスワイプ判定・効果音・距離表示(PC版)
マウスで車を「シュッ」とスワイプして走らせ、旗の手前でぴったり止める――今回はそんなゲーム「SwipeCar」を作ります。
前回のルーレットでは、回転の速さに0.96を掛け続けて「だんだん止まる」動きを作りました。SwipeCarでも同じ考え方を使います。違うのは、回転ではなく移動になること、そしてスワイプの長さで速さが決まることです。さらに、効果音と「旗までの残り距離」の表示も加えて、少しゲームらしくしていきます。
Unity 6でルーレットを作る ― クリックで回して、だんだん止める仕組み(PC版)
この記事で作るもの
- 画面の左に車、右に旗を置く
- マウスを押して横に動かし、離すと車が走り出す(スワイプ)
- スワイプが長いほど速く走り、だんだん遅くなって止まる
- 走り出すときに効果音が鳴る
- 画面に「旗までの残り距離」を表示する

| 項目 | 内容 |
|---|---|
| Unity | Unity 6(サンプルは6000.1で作成) |
| テンプレート | Universal 2D(URP) |
| 入力 | Input System(UnityEngine.InputSystem) |
| 文字の表示 | TextMeshPro(TMPro) |
| 操作 | PC版:マウスでスワイプ |
この記事では、スクリプトを2つ作ります。役割を分けておくと、どこに何を書けばよいかが分かりやすくなります。
| スクリプト | 付ける場所 | 役割 |
|---|---|---|
CarController | 車(car) | スワイプを受け取って、車を走らせる |
GameDirector | 空のオブジェクト(GameDirector) | 車と旗の距離を計算して、画面に表示する |
1. シーンを準備する
使う素材は、車(car.png)、旗(flag.png)、地面(ground.png)の画像3枚と、効果音(car_se.mp3)です。Projectウィンドウにドラッグ&ドロップして取り込み、画像をSceneビューに配置します。
| オブジェクト | Position | メモ |
|---|---|---|
| 地面(ground) | (0, -5, 0) | Scaleを(18, 1, 1)にして横に伸ばす |
| 車(car) | (-7, -3.7, 0) | 画面の左端。スクリプトを付ける |
| 旗(flag) | (7.5, -3.5, 0) | 画面の右端。ゴールの目印 |
| Main Camera | (0, 0, -10) | 背景色は好みで(サンプルは薄いベージュ) |
画像をシーンにドラッグすると、オブジェクト名が car_0 のように「画像名_番号」になることがあります。後で出てくる GameObject.Find はこの名前で探すので、Hierarchyに表示されている名前をメモしておきましょう。
2. 車を走らせる(CarController)
ルーレットのときと同じように、3つのステップで少しずつ機能を足していきます。Projectウィンドウで Create → Scripting → MonoBehaviour Script を選んで CarController を作り、Hierarchyの車にドラッグ&ドロップして付けてください。
ステップ1:クリックしたら走り出し、だんだん止まる
using UnityEngine;
using UnityEngine.InputSystem; // Mouse を使うために必要
public class CarController : MonoBehaviour
{
float speed = 0; // 1フレームあたりに進む距離
void Start()
{
// Update が1秒間に60回を目安に呼ばれるようにする
Application.targetFrameRate = 60;
}
void Update()
{
// クリックした瞬間に、速さを与える
if (Mouse.current.leftButton.wasPressedThisFrame)
{
speed = 0.2f;
}
// X方向(右)へ speed だけ進める
transform.Translate(speed, 0, 0);
// 速さを少しずつ落とす(毎フレーム98%にする)
speed *= 0.98f;
}
}
ルーレットと見比べてみてください。transform.Rotate(回転)が transform.Translate(移動)に変わっただけで、「速さを与える → 動かす → 速さを減らす」という流れはまったく同じです。
| ルーレット | SwipeCar | |
|---|---|---|
| 動かし方 | transform.Rotate(0, 0, rotSpeed) | transform.Translate(speed, 0, 0) |
| 減速 | rotSpeed *= 0.96f | speed *= 0.98f |
| 速さの決まり方 | クリックで常に10 | スワイプの長さで変わる(次のステップ) |
ステップ2:スワイプの長さで速さを決める
次に、クリックではなくスワイプで走らせます。「マウスを押した位置」と「離した位置」を記録して、その差(スワイプの長さ)を速さに変えます。
using UnityEngine;
using UnityEngine.InputSystem;
public class CarController : MonoBehaviour
{
float speed = 0;
Vector2 startPos; // マウスを押した位置
void Start()
{
Application.targetFrameRate = 60;
}
void Update()
{
if (Mouse.current.leftButton.wasPressedThisFrame)
{
// 押した瞬間の位置を覚えておく
startPos = Mouse.current.position.value;
}
else if (Mouse.current.leftButton.wasReleasedThisFrame)
{
// 離した瞬間の位置との差が、スワイプの長さ
Vector2 endPos = Mouse.current.position.value;
float swipeLength = endPos.x - startPos.x;
// スワイプの長さを速さに変換する
speed = swipeLength / 500.0f;
}
transform.Translate(speed, 0, 0);
speed *= 0.98f;
}
}
| 書き方 | 意味 |
|---|---|
wasPressedThisFrame | ボタンを押した瞬間だけ true |
wasReleasedThisFrame | ボタンを離した瞬間だけ true |
Mouse.current.position.value | 今のマウスの位置(画面のピクセル座標) |
endPos.x - startPos.x は横方向の差なので、右へスワイプすればプラス、左へスワイプすればマイナスになります。マイナスの速さになると、車は左へバックします。
ステップ3:効果音を鳴らす(完成)
最後に、走り出すときに効果音を鳴らします。スクリプトを書く前に、車にAudioSourceコンポーネントを追加しておきます。
- Hierarchyで車を選び、Inspectorの Add Component から AudioSource を追加する
- AudioSourceの Audio Resource 欄に
car_se.mp3をドラッグ&ドロップする - Play On Awake のチェックを外す(外さないと、ゲーム開始と同時に音が鳴ってしまう)
using UnityEngine;
using UnityEngine.InputSystem; // Mouse を使うために必要
public class CarController : MonoBehaviour
{
float speed = 0; // 1フレームあたりに進む距離
Vector2 startPos; // マウスを押した位置
void Start()
{
// Update が1秒間に60回を目安に呼ばれるようにする
Application.targetFrameRate = 60;
}
void Update()
{
if (Mouse.current.leftButton.wasPressedThisFrame)
{
// 押した瞬間の位置を覚えておく
startPos = Mouse.current.position.value;
}
else if (Mouse.current.leftButton.wasReleasedThisFrame)
{
// 離した瞬間の位置との差が、スワイプの長さ
Vector2 endPos = Mouse.current.position.value;
float swipeLength = endPos.x - startPos.x;
// スワイプの長さを速さに変換する
speed = swipeLength / 500.0f;
// 同じオブジェクトに付いている AudioSource で効果音を鳴らす
GetComponent<AudioSource>().Play();
}
// X方向へ進めて、少しずつ減速する
transform.Translate(speed, 0, 0);
speed *= 0.98f;
}
}
GetComponent<AudioSource>() は「このスクリプトと同じオブジェクトに付いているAudioSourceを取ってくる」という意味です。車にAudioSourceを付けたのはこのためです。
3. スワイプの長さと止まる位置の関係
では、どれくらいスワイプすれば旗の前で止まるのでしょうか。ルーレットの記事と同じように計算してみます。
速さに0.98を掛け続けたとき、止まるまでに進む距離の合計は「最初の速さ ÷ 0.02」、つまり最初の速さの50倍になります。最初の速さは「スワイプの長さ ÷ 500」なので、まとめると次のようになります。
進む距離 = (スワイプの長さ ÷ 500) × 50 = スワイプの長さ ÷ 10
| スワイプの長さ(ピクセル) | 最初の速さ | 止まるまでに進む距離の目安 |
|---|---|---|
| 100 | 0.2 | 約10 |
| 145 | 0.29 | 約14.5(ちょうど旗の位置) |
| 300 | 0.6 | 約30(画面の外まで行ってしまう) |
車は x = -7、旗は x = 7.5 にあるので、その差は14.5です。つまり約145ピクセルのスワイプで、ちょうど旗のあたりに止まる計算です。
ただし、マウスの位置は画面のピクセルで測っているので、Gameビューの大きさ(解像度)が変わると、同じ手の動きでもピクセル数が変わります。モニターによって難しさが変わるのはこのためです。

4. 残り距離を表示する(GameDirector)
次は、画面に「旗まであと何メートルか」を表示します。文字の表示にはTextMeshProを使います。
テキストを配置する
- Hierarchyで右クリックし、UI → Text – TextMeshPro を選ぶ
- 初めて使うときは「TMP Importer」ウィンドウが出るので、Import TMP Essentials を押す
- 作られたテキストの名前を
distanceに変える - Inspectorで文字の大きさ(Font Size)や幅を見やすく調整する(サンプルはFont Size 64、幅900 × 高さ80)
テキストを作ると、親として Canvas、一緒に EventSystem も自動で作られます。これらは消さないでください。
監督役のスクリプトを作る
距離の計算と表示は、車でも旗でもない「監督役」のオブジェクトに任せます。Hierarchyで Create Empty を選んで空のオブジェクトを作り、名前を GameDirector にします。そこに次のスクリプトを付けます。
using UnityEngine;
using TMPro; // TextMeshPro を使うために必要
public class GameDirector : MonoBehaviour
{
GameObject car;
GameObject flag;
GameObject distance;
void Start()
{
// シーンの中から、名前でオブジェクトを探す
car = GameObject.Find("car_0");
flag = GameObject.Find("flag_0");
distance = GameObject.Find("distance");
}
void Update()
{
// 旗と車のX座標の差が、残りの距離
float length = flag.transform.position.x - car.transform.position.x;
// 小数点以下2桁にそろえて表示する
distance.GetComponent<TextMeshProUGUI>().text = "Distance:" + length.ToString("F2") + "m";
}
}
GameObject.Find("名前") は、シーンの中から指定した名前のオブジェクトを探してくれる命令です。Start で一度だけ探しておき、Update で毎フレーム距離を計算して表示します。
GameObject.Find は、Unity 6でも使える書き方です。ただし、次のような注意点があります。
- 名前を1文字でも間違えると見つからず、null(空っぽ)になる
- Hierarchyで名前を変えると、スクリプトも直さないと動かなくなる
- 非表示(非アクティブ)のオブジェクトは見つけられない
- シーン全体を探すので、
Updateの中で毎フレーム呼ぶのは避ける
ToString(“F2") で小数点以下をそろえる
length は float 型なので、そのまま文字列にすると 3.456789 のように桁がバラバラになり、数字がチラチラして読みにくくなります。そこで ToString("F2") を使います。
| 書き方 | 3.456789 の表示 | 意味 |
|---|---|---|
length.ToString() | 3.456789 | そのまま(桁数がそろわない) |
length.ToString("F0") | 3 | 小数点以下0桁(四捨五入) |
length.ToString("F1") | 3.5 | 小数点以下1桁 |
length.ToString("F2") | 3.46 | 小数点以下2桁(今回の書き方) |
F は「Fixed-point(固定小数点)」の頭文字で、後ろの数字が小数点以下の桁数です。車が旗を通り過ぎると、距離はマイナス(例:-1.25m)になります。
5. もう一つの書き方:[SerializeField] でInspectorから設定する
GameObject.Find は手軽ですが、名前の間違いに弱いのが弱点です。そこで、Unityでよく使われるもう一つの書き方を紹介します。Inspectorで直接オブジェクトをドラッグ&ドロップして設定する方法です。
using UnityEngine;
using TMPro;
public class GameDirector : MonoBehaviour
{
// [SerializeField] を付けると、private のままInspectorに表示される
[SerializeField] GameObject car;
[SerializeField] GameObject flag;
[SerializeField] TextMeshProUGUI distance;
void Update()
{
float length = flag.transform.position.x - car.transform.position.x;
distance.text = "Distance:" + length.ToString("F2") + "m";
}
}
スクリプトを保存したら、GameDirectorを選んでInspectorを見てください。Car、Flag、Distance の3つの欄が表示されるので、Hierarchyから車・旗・テキストをそれぞれドラッグ&ドロップします。
| GameObject.Find | [SerializeField] | |
|---|---|---|
| 設定のしかた | スクリプトに名前を書く | Inspectorでドラッグ&ドロップ |
| 名前を変えたとき | スクリプトも直す必要がある | そのまま動く |
| 設定忘れ | 名前の間違いに気づきにくい | Inspectorの欄が空(None)なので気づきやすい |
| テキストの取り出し | GetComponent<TextMeshProUGUI>() が必要 | 型を TextMeshProUGUI にすれば直接使える |
![GameObject.Findは名前で探し、[SerializeField]はInspectorでドラッグ&ドロップして設定する](https://soft-rime.com/wp-content/uploads/2026/09/swipecar-illust-find-vs-serializefield-1024x576.png)
教科書どおり GameObject.Find で作ったあとに、[SerializeField] 版に書き換えてみると、両方の良さがよく分かります。[SerializeField] については次の記事でも詳しく解説しています。
6. よくあるつまずき
距離が表示されず、NullReferenceException が出る
NullReferenceException: Object reference not set to an instance of an object
GameObject.Find で指定した名前と、Hierarchyのオブジェクト名が一致していない可能性が高いです。car_0 なのか car なのか、大文字・小文字も含めて確認しましょう。[SerializeField] 版の場合は、Inspectorの欄に設定し忘れていないか確認します。
NullReferenceExceptionを感覚で直さない
効果音のところでエラーが出る
MissingComponentException: There is no 'AudioSource' attached to the "car_0" game object, but a script is trying to access it.
車にAudioSourceが付いていません。車を選んで Add Component → AudioSource を追加してください。GameDirectorなど、別のオブジェクトに付けてしまっていないかも確認しましょう。
TextMeshProUGUI が見つからないというエラーが出る
error CS0246: The type or namespace name 'TextMeshProUGUI' could not be found (are you missing a using directive or an assembly reference?)
ファイルの先頭に using TMPro; を書き忘れています。
その他のチェックポイント
- ゲームを始めた瞬間に音が鳴る → AudioSourceの Play On Awake のチェックを外す
- スワイプしても車が動かない →
CarControllerが車に付いているか、Gameビューの中でスワイプしているか確認する - テキストに日本語を入れると□(豆腐)になる → TextMeshProの標準フォントには日本語が入っていないため。日本語フォントの設定が必要
TextMeshProのテキストで全ての日本語を表示したい場合
7. 次のステップ
SwipeCarがひととおり動いたら、次のような改造に挑戦してみましょう。
/ 500.0fや0.98fの数値を変えて、難しさを調整する- 旗の前で止まったら「GOOD!」、通り過ぎたら「GAME OVER」と表示する
- 止まった位置に応じて点数を出す
止まったときの得点を出す方法や、シーンのオブジェクトとコンポーネントの関係は、次の記事で解説しています。
まとめ

transform.Translate(speed, 0, 0)とspeed *= 0.98fで、「走ってだんだん止まる」動きが作れる(ルーレットと同じ考え方)wasPressedThisFrameとwasReleasedThisFrameで押した位置と離した位置を取り、その差でスワイプの長さが分かる- 効果音は、AudioSourceを付けて
GetComponent<AudioSource>().Play()で鳴らす GameObject.Findは名前でオブジェクトを探す。手軽だが名前の間違いに弱い[SerializeField]を使うと、Inspectorから直接オブジェクトを設定できるToString("F2")で小数点以下の桁数をそろえると、数字が読みやすくなる
ルーレットで学んだ「毎フレーム少しずつ値を変える」考え方が、そのまま車の移動にも使えることが分かったと思います。同じ仕組みを別のゲームで使い回せるようになると、ゲーム作りは一気に楽しくなります。









![Unity 6.6で[field: SerializeField]を付けたプロパティがインスペクタに表示される様子](https://soft-rime.com/wp-content/uploads/2026/09/unity6-property-inspector-hyoji-150x150.png)


ディスカッション
コメント一覧
まだ、コメントがありません