Unity 6でルーレットを作る ― クリックで回して、だんだん止める仕組み(PC版)
Unityで最初に作るゲームとして定番なのが「ルーレット」です。クリックするとルーレットが回り始め、少しずつ遅くなって止まる――たったこれだけですが、オブジェクトを回す、マウスのクリックを受け取る、毎フレーム少しずつ値を変えるという、ゲーム作りの基本がそろっています。
この記事では、Unity 6とInput Systemを使って、PCのマウス操作で遊べるルーレットを作ります。コードは短いですが、「なぜ止まるのか」「なぜフレームレートを固定するのか」を数字で確かめながら読み進めていきましょう。
この記事で作るもの
- 画面の中央にルーレット盤、その上に針を置く
- マウスを左クリックするとルーレットが回り始める
- 回転はだんだん遅くなり、数秒で止まる
| 項目 | 内容 |
|---|---|
| Unity | Unity 6(サンプルは6000.1で作成) |
| テンプレート | Universal 2D(URP) |
| 入力 | Input System(UnityEngine.InputSystem) |
| 操作 | PC版:マウスの左クリック |
1. シーンを準備する
まずはルーレット盤と針の画像をシーンに並べます。画像はルーレット盤(roulette.png)と針(needle.png)の2枚です。
- Universal 2Dテンプレートで新しいプロジェクトを作る
- 2枚の画像をProjectウィンドウにドラッグ&ドロップして取り込む
- それぞれをSceneビューにドラッグして配置する
- Inspectorで位置(Position)を下の表のように設定する
| オブジェクト | Position | 役割 |
|---|---|---|
| ルーレット盤(roulette) | (0, 0, 0) | 回転させる本体。スクリプトを付ける |
| 針(needle) | (0, 3.2, 0) | 盤の真上に置く目印。動かさない |
| Main Camera | (0, 0, -10) | Projection は Orthographic(2D用) |
ポイントは、ルーレット盤を原点(0, 0, 0)に置くことです。オブジェクトは自分の中心(ピボット)を軸に回るので、盤の中心が画面の中心にあると、その場でくるくる回ってくれます。
2. スクリプトを作ってルーレットに付ける
- Projectウィンドウで右クリックし、Create → Scripting → MonoBehaviour Script を選ぶ
- 名前を
RouletteControllerにする(ファイル名とクラス名は同じにする) - できたスクリプトを、Hierarchyのルーレット盤にドラッグ&ドロップして付ける
針ではなくルーレット盤に付けるのが大事です。針に付けてしまうと、針が自分の中心を軸に回るだけになってしまいます。
3. 3つのステップでコードを書く
いきなり完成形を書くのではなく、「回す」→「クリックで回す」→「止める」の順に少しずつ機能を足していきます。1ステップごとに再生して、動きの変化を確かめてみてください。
ステップ1:とにかく回す
using UnityEngine;
public class RouletteController : MonoBehaviour
{
void Update()
{
// 毎フレーム、Z軸まわりに10度ずつ回す
transform.Rotate(0, 0, 10);
}
}
Update は1フレームに1回呼ばれるメソッドです。transform.Rotate(x, y, z) は「今の向きから、指定した角度だけ回す」命令なので、毎フレーム10度ずつ回転が積み重なっていきます。
2Dゲームでは画面がXY平面なので、画面の中でくるっと回すにはZ軸まわりに回します。正の値なら反時計回り、-10 のように負の値にすると時計回りになります。
ステップ2:クリックしたら回す
using UnityEngine;
using UnityEngine.InputSystem; // Mouse を使うために必要
public class RouletteController : MonoBehaviour
{
float rotSpeed = 0; // 回転速度(1フレームあたりに回す角度)
void Update()
{
// 左クリックした瞬間に、回転速度を与える
if (Mouse.current.leftButton.wasPressedThisFrame)
{
rotSpeed = 10;
}
transform.Rotate(0, 0, rotSpeed);
}
}
回す角度を rotSpeed という変数にしました。最初は0なので止まっていて、クリックすると10になって回り始めます。
Mouse.current.leftButton.wasPressedThisFrame は、Input Systemで「このフレームで左ボタンが押された瞬間か」を調べる書き方です。押しっぱなしでは true にならず、押した瞬間の1フレームだけ true になります。使うためには、先頭の using UnityEngine.InputSystem; が必要です。
ステップ3:だんだん遅くして止める(完成)
using UnityEngine;
using UnityEngine.InputSystem; // Mouse を使うために必要
public class RouletteController : MonoBehaviour
{
float rotSpeed = 0; // 回転速度(1フレームあたりに回す角度)
void Start()
{
// Update が1秒間に60回を目安に呼ばれるようにする
Application.targetFrameRate = 60;
}
void Update()
{
// 左クリックした瞬間に、回転速度を与える
if (Mouse.current.leftButton.wasPressedThisFrame)
{
rotSpeed = 10;
}
// 回転速度のぶんだけ回す
transform.Rotate(0, 0, rotSpeed);
// 速度を少しずつ落とす(毎フレーム96%にする)
rotSpeed *= 0.96f;
}
}
追加したのは2か所です。Start でのフレームレートの指定と、Update の最後の rotSpeed *= 0.96f; です。これでクリックすると勢いよく回り、だんだん遅くなって止まるルーレットの完成です。
4. なぜ「0.96を掛ける」と止まるのか
rotSpeed *= 0.96f; は rotSpeed = rotSpeed * 0.96f; と同じ意味です。毎フレーム「今の速度の96%」に置き換えるので、速度は4%ずつ減っていきます。60fpsで動かしたときの変化を数字で見てみましょう。
| 経過フレーム | 経過時間の目安 | rotSpeed(度/フレーム) |
|---|---|---|
| 0 | クリック直後 | 10 |
| 1 | 9.6 | |
| 2 | 9.216 | |
| 10 | 約0.17秒 | 約6.6 |
| 30 | 0.5秒 | 約2.9 |
| 60 | 1秒 | 約0.86 |
| 120 | 2秒 | 約0.075 |
| 180 | 3秒 | 約0.006 |
最初は大きく減り、遅くなるほど減り方もゆるやかになります。この「最初は速く、最後はじわじわ」という動きが、本物のルーレットらしい止まり方を生み出しています。
なお、掛け算を続けても rotSpeed は数学的には0になりません。ただ、2〜3秒後には1フレームあたり0.1度もない、目では分からないほどの回転になるので、見た目には止まったように見えます。「止まったかどうか」を判定したい場合は、rotSpeed が一定の値より小さくなったら止まったとみなす、という書き方をします。
掛ける数を変えてみよう
| 掛ける数 | 止まるまでの感じ |
|---|---|
| 0.99 | なかなか止まらず、長く回り続ける |
| 0.96 | 2〜3秒でなめらかに止まる(今回の値) |
| 0.90 | すぐにブレーキがかかって止まる |
0.96を0.99や0.90に書き換えて再生すると、数字1つでゲームの手触りが大きく変わることが体験できます。
5. なぜフレームレートを60に固定するのか
今回のコードは「1フレームごとに10度回し、1フレームごとに速度を96%にする」作りです。つまり、1秒間のフレーム数が変わると動きも変わってしまいます。
| 1秒間のフレーム数 | 1秒後の様子 |
|---|---|
| 30fps | 30フレーム分しか進まず、ゆっくり回ってゆっくり止まる |
| 60fps | 想定どおりの動き |
| 144fps | 144フレーム分進むので、速く回ってすぐ止まる |
そこで Application.targetFrameRate = 60; で「1秒に60回を目安に動かしてね」と指定し、パソコンの性能やモニターによって動きが大きく変わらないようにしています。
ただし、これはあくまで目標値です。VSync(垂直同期)の設定などによっては指定どおりにならないこともあります。本格的なゲームでは、フレームレートに左右されないよう Time.deltaTime(前のフレームからの経過秒数)を使って移動量を計算するのが一般的です。まずは「フレーム単位で動かすと、環境によって速さが変わる」ことを覚えておきましょう。
6. よくあるつまずき
Mouse が見つからないというエラーが出る
error CS0103: The name 'Mouse' does not exist in the current context
ファイルの先頭に using UnityEngine.InputSystem; を書き忘れています。Mouse はInput Systemの仲間なので、この1行がないと使えません。
ネットで見つけたコードを貼ったらエラーになった
InvalidOperationException: You are trying to read Input using the UnityEngine.Input class, but you have switched active Input handling to Input System package in Player Settings.
Input.GetMouseButtonDown(0) のような書き方は、以前のUnityで使われていたInput Managerの書き方です。Unity 6の新しいプロジェクトはInput Systemを使う設定になっているため、このエラーが出ます。今回の記事のように Mouse.current を使う書き方にそろえましょう。新旧の違いは次の記事で詳しく解説しています。
Unityの入力方法が教科書とネットで違う?― Input ManagerとInput Systemの違い
クリックしても回らない・針が回ってしまう
- スクリプトがルーレット盤に付いているか、Inspectorで確認する
- 針に付けていないか確認する(針が回ってしまう)
- Gameビューの中をクリックしているか確認する(ほかのウィンドウをクリックしても反応しない)
- Consoleにエラーが出ていないか確認する(エラーがあると再生できない)
7. もう一歩:毎回ちがう場所で止める
完成したルーレットを何度か回すと、あることに気づくかもしれません。実は、このルーレットは毎回ほぼ同じ角度だけ回って止まります。
回転の合計は 10 + 9.6 + 9.216 + … と続く足し算で、計算すると約250度になります(10 ÷ 0.04 = 250)。つまり、止まっている状態からクリックすると、いつも約250度回ってから止まるのです。これではゲームとして少し物足りませんね。
そこで、クリックしたときの速度をランダムにしてみます。Update の中を次のように変えるだけです。
if (Mouse.current.leftButton.wasPressedThisFrame)
{
// 10〜30のあいだでランダムな速度にする
rotSpeed = Random.Range(10f, 30f);
}
これで回る角度が約250〜750度のあいだで毎回変わり、どこで止まるか分からないルーレットになります。
止まった場所を判定して結果を表示する方法は、次の記事で解説しています。
Unityでルーレットを作ろう:止まった場所を判定して結果を表示する
8. スマートフォン版との違い
スマートフォンで遊べるようにする場合は、クリックの判定をタップの判定に変えます。変わるのは if の条件の1行だけです。
// PC版:マウスの左クリック
if (Mouse.current.leftButton.wasPressedThisFrame)
// スマートフォン版:画面のタップ
if (Touchscreen.current.primaryTouch.press.wasPressedThisFrame)
回転や減速の処理はまったく同じです。「入力」と「動き」を分けて考えると、操作方法が変わっても直す場所が少なくて済む、ということが分かります。
まとめ
transform.Rotate(0, 0, 角度)で、2Dのオブジェクトを画面の中で回せるMouse.current.leftButton.wasPressedThisFrameで、クリックした瞬間を受け取れる(Input System)- 速度に1より少し小さい数(0.96)を掛け続けると、自然にだんだん止まる動きになる
- フレーム単位で動かすときは、
Application.targetFrameRateで速さをそろえる - 開始速度をランダムにすると、止まる場所が毎回変わる
短いコードですが、「毎フレーム少しずつ値を変えて動きを作る」という考え方は、キャラクターの移動やジャンプ、アニメーションなど、これから作るゲームのあらゆる場面で使います。数字を変えて遊びながら、しっかり自分のものにしていきましょう。
![Unity 6.6で[field: SerializeField]を付けたプロパティがインスペクタに表示される様子](https://soft-rime.com/wp-content/uploads/2026/09/unity6-property-inspector-hyoji.png)




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





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