スポンサーリンク
Unity C#・スクリプト実装

【Unity】長押し・チャージ攻撃・ドラッグ操作の作り方完全ガイド|旧Input/Input System対応

Unity C#・スクリプト実装

Unityでクリック処理を作れるようになると、次に欲しくなるのが「押している間だけ反応する」「ためてから離す」「ドラッグした強さで飛ばす」といった操作です。

たとえば、シューティングゲームならボタンを押している間に弾を連射したくなります。アクションゲームなら、長押しで力をためて、離した瞬間にチャージ攻撃を出したくなることもあります。スマホゲームなら、指で引っ張った距離に応じてキャラクターや弾を飛ばす操作もよく使います。

ただ、ここで急に迷いやすいんですよね。

  • クリックと長押しは、どこで分ければいいの?
  • 押している時間は、どうやって計算するの?
  • 離した瞬間に攻撃するには、どの処理を書けばいいの?
  • 旧InputとInput Systemでは、考え方が変わるの?
  • ドラッグ量やパワーゲージは、どうつなげればいいの?

入力処理は、コードだけを見ると少しややこしく感じます。でも実際には、「押した瞬間」「押している間」「離した瞬間」の3つに分けると、かなり整理しやすくなります。

私も最初のころは、長押し処理を全部Updateの中に詰め込んで、気づいたら条件分岐だらけの“入力処理スパゲッティ”を作っていました。おいしくないタイプのスパゲッティです。

この記事では、Unityでよく使う長押し、チャージ攻撃、ドラッグ発射、連射、パワーゲージを、ゲーム操作のパターンとして整理していきます。旧InputとInput Systemの両方に触れながら、どの場面でどの考え方を使えばよいかを見ていきましょう。


  1. Unityの長押し操作は「押す・押し続ける・離す」で考える
    1. クリックと長押しの違い
    2. 最初に決めるべき3つのタイミング
    3. 操作パターンの判断基準
  2. Unityの入力方式は旧InputとInput Systemで選ぶ
    1. 旧Inputが向いているケース
    2. Input Systemが向いているケース
    3. 旧InputとInput Systemの選び方
  3. Unityの長押し処理は時間計測で作る
    1. 旧Inputでの基本パターン
    2. Input Systemでの基本パターン
    3. 長押し処理の判断基準
    4. よくある失敗例
  4. Unityのチャージ攻撃は離した瞬間に判定する
    1. チャージ攻撃の基本構造
    2. チャージ時間で威力を分ける
    3. Input Systemで離した瞬間に判定する
    4. Hold Interactionとの違い
    5. 正常な動きと異常な動き
    6. チャージ攻撃にパワー値を使う
    7. チャージ攻撃でやりすぎないほうがよいこと
  5. Unityのドラッグ発射は距離と方向で作る
    1. ドラッグ発射の基本構造
    2. ドラッグ量をパワーに変える
    3. 発射方向が逆になる原因
    4. Rigidbodyで発射する場合の注意点
    5. Input Systemでドラッグ量を取る
    6. ワールド座標で発射方向を決める場合
    7. ドラッグ発射で確認するチェック項目
  6. Unityの連射処理は間隔管理で作る
    1. 連射処理の基本構造
    2. コルーチンで連射する
    3. Input Systemで連射する
    4. タイマー管理とコルーチンの使い分け
    5. 連射処理でやるべきこと
    6. 連射処理でやらなくてよいこと
    7. 弾が多いゲームではオブジェクトプールも検討する
  7. Unityのパワーゲージは0〜1で表示する
    1. パワーゲージの基本
    2. Sliderでパワーゲージを作る
    3. ImageのfillAmountでパワーゲージを作る
    4. Input Systemでゲージを更新する
    5. Hold Interactionの進捗を使う場合
    6. ゲージ表示でよくある失敗
    7. ゲージの見た目を強化する
  8. Unity入力処理のよくある誤解と注意点
    1. 長押しはクリック処理の延長ではない
    2. Holdを使えばすべて解決するわけではない
    3. ドラッグ距離だけ見ればよいわけではない
    4. 連射処理は毎フレーム撃てばよいわけではない
    5. UIボタン外で離したときの挙動を忘れやすい
    6. 入力処理とUI表示を混ぜすぎない
    7. 正常な動きと異常な動きで確認する
  9. まとめ:Unityの入力操作はタイミングで分ける
  10. よくある質問(FAQ)
    1. 関連記事:

Unityの長押し操作は「押す・押し続ける・離す」で考える

Unityで長押しやチャージ攻撃を作るときは、最初に入力を3つのタイミングに分けて考えると整理しやすくなります。

  • 押した瞬間
  • 押し続けている間
  • 離した瞬間

この3つを分けずに書き始めると、「押した瞬間に攻撃したいのか」「押している間にゲージを増やしたいのか」「離した瞬間に発動したいのか」が混ざりやすくなります。コードが動いているのに、思った操作感にならないときは、だいたいこの整理が抜けています。

クリックと長押しの違い

クリック処理は、基本的に一瞬の入力を見ます。たとえば、マウスを押した瞬間に弾を撃つ、ボタンを押した瞬間にジャンプする、といった処理です。

一方で、長押し処理は「どれくらい押していたか」を使います。つまり、押されたかどうかだけではなく、押し続けた時間が大事になります。

操作見るタイミング使い道
クリック押した瞬間通常攻撃、決定、ジャンプ
長押し押している間ゲージ増加、溜め動作、押下中の演出
チャージ攻撃離した瞬間溜め時間に応じた攻撃発動
ドラッグ発射押した位置と離した位置引っ張り発射、スリングショット操作

たとえば「長押しでチャージ攻撃」と言っても、実際には3つの処理に分かれます。押した瞬間にチャージ開始、押している間にゲージ更新、離した瞬間に攻撃発動、という流れです。

最初に決めるべき3つのタイミング

実装前に、次のように日本語で処理を分けておくのがおすすめです。

  • 押した瞬間:チャージ時間を0にする
  • 押している間:チャージ時間を増やす
  • 離した瞬間:チャージ時間に応じて攻撃を出す

ドラッグ操作なら、少しだけ内容が変わります。

  • 押した瞬間:開始位置を記録する
  • 押している間:軌道や矢印を表示する
  • 離した瞬間:開始位置と終了位置の差から力を決める

このように分けると、旧InputでもInput Systemでも考え方はほとんど同じです。違うのは、入力を受け取る書き方だけです。

操作パターンの判断基準

どの処理を使えばよいか迷ったときは、作りたい操作を次の基準で分けてみてください。

  • 一瞬だけ反応すればよい:クリック処理
  • 押している間ずっと反応したい:長押し処理
  • 離した瞬間に結果を出したい:チャージ処理
  • 押した位置と離した位置を使いたい:ドラッグ処理
  • 一定間隔で繰り返したい:連射処理

たとえば、短く押したら弱攻撃、長く押したら強攻撃にしたい場合は、クリック処理だけでは足りません。押し始めから離すまでの時間を保存して、離した瞬間に「何秒押していたか」を判定する必要があります。

逆に、ボタンを押した瞬間にすぐ弾を撃つだけなら、長押し時間を計算する必要はありません。何でも長押し処理にすると、コードが少し重くなりますし、あとから読みにくくなります。

入力処理は、最初に“何をしたいか”を分けておくと一気に楽になります。コードを書く前の数十秒の整理が、あとで何十分もの修正を救ってくれることもありますよ。




Unityの入力方式は旧InputとInput Systemで選ぶ

Unityで長押しやチャージ操作を作るとき、最初に迷いやすいのが「旧Inputで作るべきか、Input Systemで作るべきか」です。

結論から言うと、小さなサンプルや学習用なら旧Inputでも十分です。一方で、ゲームパッドやスマホ操作、キーコンフィグまで考えるならInput Systemを選ぶほうが拡張しやすくなります。

どちらか一方だけが正解というより、「今作りたいものの規模」と「あとで増えそうな入力デバイス」で選ぶのが現実的です。

旧Inputが向いているケース

旧Inputは、Input.GetMouseButtonInput.GetKeyなどを使って、Update内で入力状態を確認する方法です。

たとえば、マウスの左ボタンが押されているかを毎フレーム確認して、押している間だけチャージ時間を増やす、といった書き方ができます。

  • まず動くサンプルを作りたい
  • マウスやキーボードだけで検証したい
  • Update内で処理の流れを追いたい
  • 小規模なミニゲームや学習用の実装を作りたい

旧Inputの良さは、コードの流れが見えやすいことです。押した、押している、離した、という状態を自分で確認しながら書けるので、入力処理の考え方を覚える段階ではかなり扱いやすいです。

ただし、操作パターンが増えるとUpdateの中に条件分岐が集まりやすくなります。長押し、連射、ドラッグ、UI操作を全部まとめて書くと、あとから自分で読んでも「昨日の私、何を考えてたの?」となりがちです。

Input Systemが向いているケース

Input Systemは、「攻撃」「移動」「ジャンプ」などの操作をActionとして管理する入力方式です。

旧Inputのように毎フレーム自分で確認するだけでなく、入力が始まったとき、条件を満たしたとき、入力が終わったときに処理を呼び出す設計ができます。

  • キーボード、ゲームパッド、スマホ操作に対応したい
  • 入力をAction単位で整理したい
  • 押した瞬間と離した瞬間の処理を分けたい
  • あとからキー設定やコントローラー対応を追加したい

たとえば「Fire」というActionを作っておけば、キーボードのスペースキー、ゲームパッドのボタン、スマホのタップなどを同じ攻撃操作として扱いやすくなります。

長押しやチャージ攻撃でも、startedで押し始め、canceledで離した瞬間を受け取るようにすると、処理の役割を分けやすくなります。

ただ、Input Systemは最初の設定でつまずきやすいです。Action AssetやAction Mapなどの考え方が出てくるので、いきなり全部を理解しようとすると少し大変です。まずは「入力をActionとしてまとめる仕組みなんだな」くらいから入ると、気持ちがだいぶ楽になります。

旧InputとInput Systemの選び方

迷ったときは、次のように選ぶと判断しやすいです。

目的おすすめ
学習用にすぐ試したい旧Input
マウスだけで小さく作りたい旧Input
ゲームパッドにも対応したいInput System
スマホや複数デバイスを想定したいInput System
キーコンフィグまで作りたいInput System

この記事では、まず旧Inputで考え方をつかみ、そのあとInput Systemでも同じ考え方をどう使うかを見ていく流れにします。

Input Systemの導入やAction設定で迷っている場合は、先にこちらの記事で全体像を確認しておくと進めやすいです。

ゲームパッド対応まで確認したい場合は、実機でボタン入力を試せる環境があるとかなり便利です。キーボードでは気づきにくい「押し込み感」や「ボタンを離すタイミング」も確認できます。

Xbox ワイヤレス コントローラー
✅ Amazonでチェックする✅ 楽天でチェックする




Unityの長押し処理は時間計測で作る

Unityで長押し処理を作るときの基本は、「押している時間」を変数にためることです。

クリックなら押した瞬間だけ見れば十分ですが、長押しでは「何秒押していたか」が必要になります。ここを分けて考えると、チャージ攻撃やパワーゲージにもつなげやすくなります。

旧Inputでの基本パターン

旧Inputで長押し時間を計測する場合は、GetMouseButtonDownGetMouseButtonGetMouseButtonUpを使い分けます。

  • GetMouseButtonDown:押した瞬間
  • GetMouseButton:押している間
  • GetMouseButtonUp:離した瞬間

長押し時間をためるだけなら、考え方はかなりシンプルです。

using UnityEngine;

public class HoldInputExample : MonoBehaviour
{
    private float holdTime = 0f;
    private bool isHolding = false;

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            holdTime = 0f;
            isHolding = true;
            Debug.Log("押し始め");
        }

        if (Input.GetMouseButton(0) && isHolding)
        {
            holdTime += Time.deltaTime;
            Debug.Log("長押し中: " + holdTime);
        }

        if (Input.GetMouseButtonUp(0))
        {
            isHolding = false;
            Debug.Log("離した。押していた時間: " + holdTime);
        }
    }
}

Time.deltaTimeは、前のフレームから今のフレームまでに経過した時間です。これを押している間だけ足していくことで、「何秒押していたか」を計算できます。

大事なのは、押し始めた瞬間にholdTimeを0へ戻すことです。これを忘れると、前回の長押し時間が残ってしまい、次の入力でいきなり強い攻撃が出るような不思議現象が起きます。ゲーム側からすると必殺技ですが、作っている側からするとバグです。

Input Systemでの基本パターン

Input Systemでは、入力の状態をイベントとして受け取れます。長押し時間を計測する場合は、startedで開始時刻を記録し、canceledで離した時刻との差を取る考え方が使いやすいです。

using UnityEngine;
using UnityEngine.InputSystem;

public class HoldInputSystemExample : MonoBehaviour
{
    [SerializeField] private InputAction fireAction;

    private float startTime;

    private void OnEnable()
    {
        fireAction.started += OnFireStarted;
        fireAction.canceled += OnFireCanceled;
        fireAction.Enable();
    }

    private void OnDisable()
    {
        fireAction.started -= OnFireStarted;
        fireAction.canceled -= OnFireCanceled;
        fireAction.Disable();
    }

    private void OnFireStarted(InputAction.CallbackContext context)
    {
        startTime = Time.time;
        Debug.Log("押し始め");
    }

    private void OnFireCanceled(InputAction.CallbackContext context)
    {
        float holdTime = Time.time - startTime;
        Debug.Log("離した。押していた時間: " + holdTime);
    }
}

この方法では、押している間ずっと時間を足し続けるのではなく、「開始時刻」と「終了時刻」の差で長押し時間を求めています。

ゲージを毎フレーム更新したい場合は、別途Updateで現在の経過時間を表示することもあります。ただし、攻撃の発動だけなら、離した瞬間に差分を計算するだけでも十分です。

長押し処理の判断基準

長押し処理では、何をしたいかによって実装方法を少し変えると扱いやすくなります。

やりたいこと考え方
押している時間を表示したい押下中に毎フレーム更新する
離した瞬間に威力を決めたい開始時刻と終了時刻の差を使う
一定時間を超えたら成功にしたいしきい値を決めて判定する
押している間だけ演出を出したい押下中フラグで表示を切り替える

たとえば、「1秒以上押したら強攻撃」にしたいなら、離した瞬間にholdTime >= 1fのように判定します。

if (holdTime >= 1f)
{
    Debug.Log("強攻撃");
}
else
{
    Debug.Log("通常攻撃");
}

このように、長押し処理は難しい特別な仕組みではありません。押した時間を保存して、必要なタイミングで使うだけです。

よくある失敗例

長押し処理でよくある失敗は、入力のタイミングを混ぜてしまうことです。

  • GetMouseButtonDownだけで長押し時間を取ろうとする
  • 押し始めに時間をリセットしていない
  • 離したあとも押下中フラグがtrueのままになっている
  • Input Systemのperformedだけで離した瞬間の処理を作ろうとする

GetMouseButtonDownは押した瞬間だけ反応するので、押し続けた時間をそのまま取ることはできません。長押し中の時間を増やすならGetMouseButton、離した瞬間に確定するならGetMouseButtonUpを使います。

Input Systemでも同じです。押し始め、条件達成、キャンセルや解放は別のタイミングとして扱います。特にチャージ攻撃では、「いつ発動したいのか」を先に決めてからイベントを選ぶと迷いにくくなります。




Unityのチャージ攻撃は離した瞬間に判定する

Unityでチャージ攻撃を作るときは、「長押ししている間に攻撃する」のではなく、「離した瞬間に、ためた時間を見て攻撃内容を決める」と考えると作りやすくなります。

たとえば、ボタンを押したらチャージ開始、押している間はゲージが増える、ボタンを離したら溜め時間に応じて攻撃を出す、という流れです。

この流れを分けずに作ると、押した瞬間に攻撃が出てしまったり、離しても反応しなかったり、前回のチャージ量が残ったりします。チャージ攻撃は見た目が派手なぶん、内部の状態管理は少し丁寧にしてあげましょう。

チャージ攻撃の基本構造

チャージ攻撃は、次の4段階で考えると分かりやすいです。

  1. 押した瞬間にチャージを開始する
  2. 押している間にチャージ時間を増やす
  3. 離した瞬間にチャージ時間を判定する
  4. 攻撃後にチャージ時間をリセットする

旧Inputで書く場合は、次のような形になります。

using UnityEngine;

public class ChargeAttackOldInput : MonoBehaviour
{
    [SerializeField] private float weakChargeTime = 0.3f;
    [SerializeField] private float strongChargeTime = 1.5f;
    [SerializeField] private float maxChargeTime = 2f;

    private float chargeTime = 0f;
    private bool isCharging = false;

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            StartCharge();
        }

        if (Input.GetMouseButton(0) && isCharging)
        {
            UpdateCharge();
        }

        if (Input.GetMouseButtonUp(0) && isCharging)
        {
            ReleaseCharge();
        }
    }

    private void StartCharge()
    {
        chargeTime = 0f;
        isCharging = true;

        Debug.Log("チャージ開始");
    }

    private void UpdateCharge()
    {
        chargeTime += Time.deltaTime;
        chargeTime = Mathf.Min(chargeTime, maxChargeTime);

        Debug.Log("チャージ中: " + chargeTime);
    }

    private void ReleaseCharge()
    {
        isCharging = false;

        if (chargeTime < weakChargeTime)
        {
            NormalAttack();
        }
        else if (chargeTime < strongChargeTime)
        {
            MediumChargeAttack();
        }
        else
        {
            StrongChargeAttack();
        }

        chargeTime = 0f;
    }

    private void NormalAttack()
    {
        Debug.Log("通常攻撃");
    }

    private void MediumChargeAttack()
    {
        Debug.Log("中チャージ攻撃");
    }

    private void StrongChargeAttack()
    {
        Debug.Log("最大チャージ攻撃");
    }
}

ポイントは、ReleaseChargeの中で攻撃を決めていることです。つまり、「何秒ためたか」を離した瞬間に確認しています。

Mathf.Minで最大チャージ時間を超えないようにしているのも大切です。上限を作らないと、ずっと押しっぱなしにしたときにチャージ時間が増え続けます。威力計算にそのまま使うと、神の一撃みたいな数値になってしまうかもしれません。

チャージ時間で威力を分ける

チャージ攻撃では、「何秒以上なら強攻撃にするか」を決めておくと調整しやすくなります。

押していた時間攻撃の例使いどころ
0.3秒未満通常攻撃短押しでも反応させたい場合
0.3秒〜1.5秒未満中チャージ攻撃軽い溜め攻撃を入れたい場合
1.5秒以上最大チャージ攻撃強力な一撃や必殺技にしたい場合

この秒数は、ゲームのテンポによって変えてください。アクションゲームなら1秒でも長く感じることがありますし、パズル寄りのゲームなら2〜3秒のチャージでも自然に感じる場合があります。

私なら、まずは最大チャージを1.5〜2秒くらいにして試します。実際に触ってみて「ちょっと遅いな」と感じたら短く、「すぐ最大になりすぎるな」と感じたら長くします。数値は最初から正解を当てにいくより、触って調整するほうが早いです。

Input Systemで離した瞬間に判定する

Input Systemでチャージ攻撃を作る場合も、考え方は同じです。

  • started:押し始めた瞬間
  • canceled:離した瞬間

押し始めた時刻を保存し、離した瞬間に現在時刻との差を計算します。

using UnityEngine;
using UnityEngine.InputSystem;

public class ChargeAttackInputSystem : MonoBehaviour
{
    [SerializeField] private InputAction attackAction;

    [SerializeField] private float weakChargeTime = 0.3f;
    [SerializeField] private float strongChargeTime = 1.5f;
    [SerializeField] private float maxChargeTime = 2f;

    private float chargeStartTime;
    private bool isCharging = false;

    private void OnEnable()
    {
        attackAction.started += OnAttackStarted;
        attackAction.canceled += OnAttackCanceled;
        attackAction.Enable();
    }

    private void OnDisable()
    {
        attackAction.started -= OnAttackStarted;
        attackAction.canceled -= OnAttackCanceled;
        attackAction.Disable();
    }

    private void Update()
    {
        if (isCharging)
        {
            float currentChargeTime = GetCurrentChargeTime();
            Debug.Log("チャージ中: " + currentChargeTime);
        }
    }

    private void OnAttackStarted(InputAction.CallbackContext context)
    {
        chargeStartTime = Time.time;
        isCharging = true;

        Debug.Log("チャージ開始");
    }

    private void OnAttackCanceled(InputAction.CallbackContext context)
    {
        if (!isCharging)
        {
            return;
        }

        float chargeTime = GetCurrentChargeTime();

        isCharging = false;
        ExecuteChargeAttack(chargeTime);
    }

    private float GetCurrentChargeTime()
    {
        float elapsedTime = Time.time - chargeStartTime;
        return Mathf.Min(elapsedTime, maxChargeTime);
    }

    private void ExecuteChargeAttack(float chargeTime)
    {
        if (chargeTime < weakChargeTime)
        {
            NormalAttack();
        }
        else if (chargeTime < strongChargeTime)
        {
            MediumChargeAttack();
        }
        else
        {
            StrongChargeAttack();
        }
    }

    private void NormalAttack()
    {
        Debug.Log("通常攻撃");
    }

    private void MediumChargeAttack()
    {
        Debug.Log("中チャージ攻撃");
    }

    private void StrongChargeAttack()
    {
        Debug.Log("最大チャージ攻撃");
    }
}

このコードでは、Update内でチャージ中の時間を確認しています。ただし、攻撃を発動しているのはOnAttackCanceledの中です。

つまり、ゲージ表示や演出の更新は押している間、攻撃の確定は離した瞬間、というように役割を分けています。この分け方をしておくと、あとからUIゲージやエフェクトを足すときも混乱しにくいです。

Hold Interactionとの違い

Input Systemには、Hold Interactionという長押し判定の仕組みがあります。一定時間押し続けたかを判定したいときには便利です。

ただし、チャージ攻撃でよくある「離した瞬間に、溜めた時間で威力を変える」処理では、startedcanceledで時間差を取るほうが分かりやすい場面もあります。

使い分けの目安は、次のように考えるとよいです。

  • 一定時間押したら成功にしたい:Hold Interactionが便利
  • 押していた時間に応じて威力を細かく変えたい:時間差分を自分で計算する
  • 離した瞬間に必ず何かを発動したい:canceledを使う

たとえば、「1秒押したら扉を開ける」ならHold Interactionが向いています。一方で、「0.5秒なら小攻撃、1.2秒なら中攻撃、2秒なら大攻撃」のように段階を分けるなら、自分でチャージ時間を持つほうが調整しやすいです。

正常な動きと異常な動き

チャージ攻撃は、完成しているように見えても、触ってみると細かい違和感が出やすい処理です。次のように、正常な動きと異常な動きを分けて確認すると原因を見つけやすくなります。

正常な動きは、次の状態です。

  • 押した瞬間にチャージが始まる
  • 押している間だけチャージ時間が増える
  • 最大チャージ時間を超えても値が暴走しない
  • 離した瞬間に攻撃が発動する
  • 攻撃後にチャージ時間がリセットされる

逆に、次のような動きがある場合は見直しが必要です。

  • 押した瞬間に攻撃が出てしまう
  • 離しても攻撃が出ない
  • 一度最大チャージすると次回も最大攻撃になる
  • ボタンを押していないのにゲージが増える
  • 短く押しただけなのに強攻撃になる

特に多いのは、攻撃後にchargeTimeisChargingを戻し忘れるパターンです。チャージ処理では、開始・更新・発動・リセットを1セットとして考えると安全です。

チャージ攻撃にパワー値を使う

攻撃を3段階に分けるだけでなく、チャージ時間をそのまま攻撃力に変換することもできます。

この場合は、チャージ時間を0〜1の割合に変換してから、攻撃力に掛けると扱いやすくなります。

private float CalculateChargeRate(float chargeTime)
{
    return Mathf.Clamp01(chargeTime / maxChargeTime);
}

private float CalculateAttackPower(float chargeTime)
{
    float minPower = 10f;
    float maxPower = 50f;

    float chargeRate = CalculateChargeRate(chargeTime);
    return Mathf.Lerp(minPower, maxPower, chargeRate);
}

Mathf.Clamp01は、値を0〜1の範囲に収めるために使います。Mathf.Lerpは、最小値から最大値までを割合に応じてなめらかに変える処理です。

たとえば、最大チャージ時間が2秒で、1秒だけ押した場合はchargeRateが0.5になります。その結果、攻撃力も最小と最大の中間あたりになります。

段階式の攻撃は分かりやすく、調整もしやすいです。一方で、割合で攻撃力を変える方法は、プレイヤーの押し時間がそのまま結果に反映されるので、手触りを作り込みやすくなります。

チャージ攻撃でやりすぎないほうがよいこと

チャージ攻撃は楽しいので、ついエフェクト、音、カメラ揺れ、アニメーション、弾の変化などを一気に入れたくなります。気持ちはとても分かります。私もよく盛ります。

ただ、最初から全部入れると、どこでバグが起きているのか分かりにくくなります。

最初は次の順番で作るのがおすすめです。

  1. 押した時間をログで表示する
  2. 離した瞬間に攻撃名をログで表示する
  3. チャージ時間で攻撃を分岐する
  4. ゲージを表示する
  5. 攻撃エフェクトやSEを追加する

まずはログだけで正しく動くことを確認してから、見た目を足していくと失敗しにくいです。入力処理の土台が安定していると、あとから演出を追加しても崩れにくくなります。

Unityの基本からC#の書き方まで手元で確認しながら進めたい場合は、書籍を1冊持っておくと調べ直しがしやすいです。ネット検索だけだと情報が点になりやすいので、基礎をまとめて見返せるものがあると安心感があります。

Unityの教科書 Unity 6完全対応版
✅ Amazonでチェックする✅ 楽天でチェックする




Unityのドラッグ発射は距離と方向で作る

Unityでドラッグ発射を作るときは、「押した位置」と「離した位置」の差を使います。

長押し処理では時間が主役でしたが、ドラッグ操作では距離と方向が主役です。マウスや指でどれくらい引っ張ったか、どの向きに引っ張ったかを計算して、その結果を発射パワーや発射方向に変換します。

たとえば、弓、パチンコ、スリングショット、ゴルフゲーム、ビリヤード風の操作などでよく使う考え方です。

ドラッグ発射の基本構造

ドラッグ発射は、次の流れで作ります。

  1. 押した瞬間に開始位置を保存する
  2. 押している間に現在位置を確認する
  3. 離した瞬間に終了位置を取得する
  4. 開始位置と終了位置の差を計算する
  5. 差分を発射方向とパワーに使う

まずは、旧Inputでマウスドラッグ量を取得する基本コードを見てみましょう。

using UnityEngine;

public class DragShootOldInput : MonoBehaviour
{
    [SerializeField] private Rigidbody targetRigidbody;
    [SerializeField] private float powerMultiplier = 0.05f;
    [SerializeField] private float maxPower = 20f;

    private Vector3 startMousePosition;
    private Vector3 endMousePosition;
    private bool isDragging = false;

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            StartDrag();
        }

        if (Input.GetMouseButton(0) && isDragging)
        {
            UpdateDrag();
        }

        if (Input.GetMouseButtonUp(0) && isDragging)
        {
            EndDrag();
        }
    }

    private void StartDrag()
    {
        startMousePosition = Input.mousePosition;
        isDragging = true;

        Debug.Log("ドラッグ開始: " + startMousePosition);
    }

    private void UpdateDrag()
    {
        Vector3 currentMousePosition = Input.mousePosition;
        Vector3 dragVector = currentMousePosition - startMousePosition;

        Debug.Log("ドラッグ中: " + dragVector);
    }

    private void EndDrag()
    {
        endMousePosition = Input.mousePosition;
        isDragging = false;

        Vector3 dragVector = endMousePosition - startMousePosition;
        Shoot(dragVector);
    }

    private void Shoot(Vector3 dragVector)
    {
        Vector3 direction = new Vector3(dragVector.x, 0f, dragVector.y).normalized;
        float power = Mathf.Clamp(dragVector.magnitude * powerMultiplier, 0f, maxPower);

        targetRigidbody.AddForce(direction * power, ForceMode.Impulse);

        Debug.Log("発射方向: " + direction + " / パワー: " + power);
    }
}

このコードでは、マウスを押した位置をstartMousePositionに保存し、離した位置との差をdragVectorとして計算しています。

dragVector.magnitudeはドラッグした距離です。距離が長いほどパワーが大きくなります。ただし、そのまま使うと強くなりすぎる場合があるので、Mathf.Clampで上限を決めています。

ドラッグ量をパワーに変える

ドラッグ発射で大切なのは、ドラッグ距離をそのまま力にしないことです。

画面上のマウス座標はピクセル単位なので、少しドラッグしただけでも数十〜数百の値になります。その値をそのままAddForceに入れると、オブジェクトが宇宙旅行へ出発することがあります。帰ってきません。

そのため、次のように調整用の変数を用意します。

  • powerMultiplier:ドラッグ距離をどれくらいの力に変換するか
  • maxPower:最大パワー
  • minDragDistance:短すぎるドラッグを無視する距離

短すぎるドラッグを無効にしたい場合は、次のように判定できます。

private void Shoot(Vector3 dragVector)
{
    float minDragDistance = 30f;

    if (dragVector.magnitude < minDragDistance)
    {
        Debug.Log("ドラッグ距離が短すぎるため発射しません");
        return;
    }

    Vector3 direction = new Vector3(dragVector.x, 0f, dragVector.y).normalized;
    float power = Mathf.Clamp(dragVector.magnitude * powerMultiplier, 0f, maxPower);

    targetRigidbody.AddForce(direction * power, ForceMode.Impulse);
}

このようにしておくと、クリックしただけで弾が飛ぶような誤発射を防げます。スマホ操作でも、指が少し動いただけで反応してしまう問題を減らせます。

発射方向が逆になる原因

ドラッグ発射でよくある失敗が、「思った方向と逆に飛ぶ」問題です。

これは、差分ベクトルの向きが原因です。

  • endMousePosition - startMousePosition:ドラッグした方向に飛ばす
  • startMousePosition - endMousePosition:引っ張った反対方向に飛ばす

たとえば、指やマウスを右へドラッグしたときに右へ飛ばしたいなら、end - startを使います。

Vector3 dragVector = endMousePosition - startMousePosition;

一方で、パチンコやスリングショットのように「左へ引っ張ったら右へ飛ぶ」操作にしたいなら、start - endを使います。

Vector3 dragVector = startMousePosition - endMousePosition;

どちらが正しいかは、ゲームの操作感によって変わります。コードを書く前に、「引っ張った方向へ飛ばすのか」「引っ張った反対方向へ飛ばすのか」を決めておくと迷いません。

操作感差分の取り方
ドラッグした方向へ飛ぶend - startフリック発射、スワイプ移動
引っ張った反対へ飛ぶstart - end弓、パチンコ、スリングショット

Rigidbodyで発射する場合の注意点

物理挙動でオブジェクトを飛ばしたい場合は、Rigidbody.AddForceを使います。

一瞬で力を加えて発射するなら、ForceMode.Impulseを使うと直感的な動きになりやすいです。

targetRigidbody.AddForce(direction * power, ForceMode.Impulse);

ただし、AddForceが効かない、または思ったように飛ばない場合は、入力処理ではなくRigidbody側の設定が原因かもしれません。

  • 対象オブジェクトにRigidbodyが付いているか
  • RigidbodyのMassが極端に大きくないか
  • DragやAngular Dragが強すぎないか
  • Constraintsで位置が固定されていないか
  • Is Kinematicが有効になっていないか

特にIs Kinematicが有効なRigidbodyは、物理演算で動かす前提ではありません。AddForceで飛ばしたい場合は、基本的にはオフにしておきます。

Input Systemでドラッグ量を取る

Input Systemでも、ドラッグ発射の考え方は同じです。押した瞬間にポインター位置を保存し、離した瞬間に現在位置との差を計算します。

ここでは、クリックやタップに使うActionと、ポインター位置を読むActionを分けて考えます。

using UnityEngine;
using UnityEngine.InputSystem;

public class DragShootInputSystem : MonoBehaviour
{
    [SerializeField] private InputAction pressAction;
    [SerializeField] private InputAction positionAction;

    [SerializeField] private Rigidbody targetRigidbody;
    [SerializeField] private float powerMultiplier = 0.05f;
    [SerializeField] private float maxPower = 20f;
    [SerializeField] private float minDragDistance = 30f;

    private Vector2 startPosition;
    private bool isDragging = false;

    private void OnEnable()
    {
        pressAction.started += OnPressStarted;
        pressAction.canceled += OnPressCanceled;

        pressAction.Enable();
        positionAction.Enable();
    }

    private void OnDisable()
    {
        pressAction.started -= OnPressStarted;
        pressAction.canceled -= OnPressCanceled;

        pressAction.Disable();
        positionAction.Disable();
    }

    private void OnPressStarted(InputAction.CallbackContext context)
    {
        startPosition = positionAction.ReadValue<Vector2>();
        isDragging = true;

        Debug.Log("ドラッグ開始: " + startPosition);
    }

    private void OnPressCanceled(InputAction.CallbackContext context)
    {
        if (!isDragging)
        {
            return;
        }

        Vector2 endPosition = positionAction.ReadValue<Vector2>();
        Vector2 dragVector = endPosition - startPosition;

        isDragging = false;
        Shoot(dragVector);
    }

    private void Shoot(Vector2 dragVector)
    {
        if (dragVector.magnitude < minDragDistance)
        {
            Debug.Log("ドラッグ距離が短すぎるため発射しません");
            return;
        }

        Vector3 direction = new Vector3(dragVector.x, 0f, dragVector.y).normalized;
        float power = Mathf.Clamp(dragVector.magnitude * powerMultiplier, 0f, maxPower);

        targetRigidbody.AddForce(direction * power, ForceMode.Impulse);

        Debug.Log("発射方向: " + direction + " / パワー: " + power);
    }
}

positionAction.ReadValue<Vector2>()で、現在のポインター位置を取得しています。マウスでもタッチでも、設定次第で同じような考え方を使えます。

ただし、スマホの複数指操作や、UIの上をタップしたときの扱いまで入れると少し話が広がります。まずは1本指、またはマウス1つでドラッグ量を取れるようにしてから、必要に応じて拡張していくのがおすすめです。

ワールド座標で発射方向を決める場合

ここまでのコードでは、画面上のドラッグ量を使って発射方向を作っています。トップダウン視点や簡単な発射処理なら、この方法でも試しやすいです。

ただし、3D空間で「クリックしたワールド位置へ向けて飛ばす」ような処理では、画面座標をそのまま使うとズレることがあります。

その場合は、カメラからRaycastを飛ばして、地面や対象オブジェクト上の位置を取得します。

private bool TryGetMouseWorldPosition(out Vector3 worldPosition)
{
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

    if (Physics.Raycast(ray, out RaycastHit hit))
    {
        worldPosition = hit.point;
        return true;
    }

    worldPosition = Vector3.zero;
    return false;
}

このようにすると、画面上のマウス位置ではなく、ゲーム内の実際の位置を取得できます。

2DゲームならCamera.main.ScreenToWorldPointを使うケースもあります。3DならRaycast、2DならScreenToWorldPoint、と覚えておくと最初の判断がしやすいです。

ドラッグ発射で確認するチェック項目

ドラッグ発射が思ったように動かないときは、次の順番で確認してみてください。

  • 押した瞬間の位置が保存されているか
  • 離した瞬間の位置が取得できているか
  • 差分ベクトルの向きが合っているか
  • ドラッグ距離が短すぎて無効になっていないか
  • パワー倍率が小さすぎないか、大きすぎないか
  • Rigidbodyの設定で動きが止められていないか

いきなり完成形を目指すより、まずはDebug.Logで開始位置、終了位置、ドラッグ量、パワーを表示すると原因を見つけやすいです。

ドラッグ操作は、見た目にはシンプルでも「画面座標」「方向」「距離」「物理挙動」が絡みます。動きがおかしいときは、慌ててコード全体を書き直すより、どの値が想定と違うのかを1つずつ確認していきましょう。




Unityの連射処理は間隔管理で作る

Unityで「ボタンを押している間、n秒おきに弾を撃つ」処理を作るときは、押下状態だけでなく「発射間隔」を管理する必要があります。

押している間ずっと発射したいからといって、毎フレーム弾を生成すると大変です。1秒間に60フレーム動いている環境なら、1秒で60発の弾が出ることもあります。マシンガンというより、弾幕の大洪水です。

連射処理では、「押しているか」と「次に撃てる時間か」を分けて考えると安定します。

連射処理の基本構造

連射処理は、次の流れで作ります。

  1. ボタンを押した瞬間に連射を開始する
  2. 一定時間ごとに弾を発射する
  3. ボタンを離した瞬間に連射を止める
  4. 連射が二重に始まらないようにする

まずは、旧Inputでタイマーを使う方法を見てみましょう。

using UnityEngine;

public class AutoFireOldInput : MonoBehaviour
{
    [SerializeField] private GameObject bulletPrefab;
    [SerializeField] private Transform firePoint;
    [SerializeField] private float fireInterval = 0.2f;

    private float fireTimer = 0f;

    private void Update()
    {
        if (Input.GetMouseButton(0))
        {
            fireTimer -= Time.deltaTime;

            if (fireTimer <= 0f)
            {
                Fire();
                fireTimer = fireInterval;
            }
        }

        if (Input.GetMouseButtonUp(0))
        {
            fireTimer = 0f;
        }
    }

    private void Fire()
    {
        Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
        Debug.Log("発射");
    }
}

このコードでは、マウスボタンを押している間だけfireTimerを減らしています。そして、タイマーが0以下になったら弾を発射し、またfireIntervalの値に戻しています。

fireIntervalが0.2なら、約0.2秒ごとに1発撃つイメージです。0.1にすると速い連射、0.5にするとゆっくりした連射になります。

コルーチンで連射する

連射処理は、コルーチンでも作れます。コルーチンは「処理を実行して、少し待って、また実行する」という流れを書きやすい仕組みです。

押した瞬間にコルーチンを開始し、離した瞬間に止めると、連射の流れが見やすくなります。

using System.Collections;
using UnityEngine;

public class AutoFireCoroutine : MonoBehaviour
{
    [SerializeField] private GameObject bulletPrefab;
    [SerializeField] private Transform firePoint;
    [SerializeField] private float fireInterval = 0.2f;

    private Coroutine fireCoroutine;

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            StartAutoFire();
        }

        if (Input.GetMouseButtonUp(0))
        {
            StopAutoFire();
        }
    }

    private void StartAutoFire()
    {
        if (fireCoroutine != null)
        {
            return;
        }

        fireCoroutine = StartCoroutine(AutoFire());
    }

    private void StopAutoFire()
    {
        if (fireCoroutine == null)
        {
            return;
        }

        StopCoroutine(fireCoroutine);
        fireCoroutine = null;
    }

    private IEnumerator AutoFire()
    {
        while (true)
        {
            Fire();
            yield return new WaitForSeconds(fireInterval);
        }
    }

    private void Fire()
    {
        Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
        Debug.Log("発射");
    }
}

ここで大事なのは、fireCoroutinenullでない場合は新しく開始しないことです。

このチェックを入れないと、ボタンを押すたびにコルーチンが増えて、1つのボタン入力で何重にも弾が出ることがあります。見た目は強そうですが、バグとしてはなかなか手強いタイプです。

Input Systemで連射する

Input Systemで連射を作る場合も、考え方は同じです。startedで連射を開始し、canceledで停止します。

using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;

public class AutoFireInputSystem : MonoBehaviour
{
    [SerializeField] private InputAction fireAction;

    [SerializeField] private GameObject bulletPrefab;
    [SerializeField] private Transform firePoint;
    [SerializeField] private float fireInterval = 0.2f;

    private Coroutine fireCoroutine;

    private void OnEnable()
    {
        fireAction.started += OnFireStarted;
        fireAction.canceled += OnFireCanceled;
        fireAction.Enable();
    }

    private void OnDisable()
    {
        fireAction.started -= OnFireStarted;
        fireAction.canceled -= OnFireCanceled;
        fireAction.Disable();

        StopAutoFire();
    }

    private void OnFireStarted(InputAction.CallbackContext context)
    {
        StartAutoFire();
    }

    private void OnFireCanceled(InputAction.CallbackContext context)
    {
        StopAutoFire();
    }

    private void StartAutoFire()
    {
        if (fireCoroutine != null)
        {
            return;
        }

        fireCoroutine = StartCoroutine(AutoFire());
    }

    private void StopAutoFire()
    {
        if (fireCoroutine == null)
        {
            return;
        }

        StopCoroutine(fireCoroutine);
        fireCoroutine = null;
    }

    private IEnumerator AutoFire()
    {
        while (true)
        {
            Fire();
            yield return new WaitForSeconds(fireInterval);
        }
    }

    private void Fire()
    {
        Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
        Debug.Log("発射");
    }
}

OnDisableの中でもStopAutoFireを呼んでいるのは、オブジェクトが無効になったときに連射処理を残さないためです。

ゲーム中に武器を切り替えたり、プレイヤーが倒れたり、シーンを移動したりすると、入力処理が途中で止まることがあります。そのときにコルーチンだけ残ると、見えないところで処理が続いてしまう場合があります。

タイマー管理とコルーチンの使い分け

連射処理は、タイマーでもコルーチンでも作れます。どちらが絶対に正しいというより、作りたい処理の複雑さで選ぶとよいです。

方法向いているケース注意点
タイマー管理武器状態やゲーム状態と細かく連動したいUpdate内の条件が増えやすい
コルーチン一定間隔で同じ処理を繰り返したい二重起動と停止忘れに注意する

初心者のうちは、コルーチンのほうが「発射する、待つ、また発射する」という流れを読みやすいと思います。

一方で、中級者以上で武器ごとに発射間隔、弾数、リロード、クールタイムなどをまとめて管理したい場合は、タイマー管理や武器クラスを分ける設計のほうが扱いやすくなることもあります。

連射処理でやるべきこと

連射処理では、次の3つを必ず意識しておくと安定します。

  • 発射間隔を変数にして調整できるようにする
  • ボタンを離したら確実に停止する
  • 連射処理が二重に始まらないようにする

特にfireIntervalは、あとから調整する可能性が高い値です。コードの中に直接0.2fと書くより、[SerializeField]でInspectorから変更できるようにしておくと便利です。

武器ごとに発射間隔を変える場合も、変数にしておくと調整が楽になります。ハンドガンは0.4秒、マシンガンは0.1秒、ショットガンは0.8秒、というように分けられます。

連射処理でやらなくてよいこと

最初から複雑な武器システムを作ろうとすると、入力処理の理解がぼやけやすくなります。

まずは、次のようなことを避けるだけでも十分です。

  • 押している間、毎フレームInstantiateする
  • StartCoroutineを押下中ずっと呼び続ける
  • 発射間隔をコード内に直接いくつも書く
  • 停止処理を書かずに開始処理だけ作る
  • 弾の生成、音、エフェクト、反動を最初から全部混ぜる

特に毎フレームInstantiateは避けたいところです。弾が多いゲームでは、生成と破棄が増えるほど処理負荷も上がりやすくなります。

まずは「一定間隔で発射できる」「離したら止まる」ことを確認してから、弾の見た目、効果音、反動、弾数制限などを足していくと、原因を追いやすいです。

弾が多いゲームではオブジェクトプールも検討する

連射処理で大量の弾を出す場合、最終的にはオブジェクトプールも検討したいです。

オブジェクトプールとは、弾を毎回新しく作って消すのではなく、あらかじめ用意した弾を使い回す仕組みです。シューティングゲームやアクションゲームのように弾やエフェクトが多いゲームでは、処理負荷を抑えやすくなります。

ただし、最初からオブジェクトプールまで入れると、入力処理とは別の難しさが増えます。まずはInstantiateで連射の流れを確認し、弾数が増えて重くなってきたらプール化を考える、という順番で大丈夫です。

連射処理の主役は、「押している間に、決めた間隔で実行する」ことです。そこが安定してから最適化へ進むと、実装の迷子になりにくいですよ。




Unityのパワーゲージは0〜1で表示する

Unityでチャージ攻撃を作るなら、パワーゲージも一緒に用意すると操作感がかなり良くなります。

プレイヤーは、内部のchargeTimeを見ることができません。どれくらい溜まっているのかが画面に出ていないと、「今、強攻撃になるの?まだ?」と迷いやすくなります。

パワーゲージは、入力処理そのものではなく「今の状態を見えるようにするUI」です。ここを分けて考えると、コードも整理しやすくなります。

パワーゲージの基本

パワーゲージは、現在のチャージ時間を最大チャージ時間で割って、0〜1の値に変換して表示します。

float chargeRate = currentChargeTime / maxChargeTime;

たとえば、最大チャージ時間が2秒で、現在のチャージ時間が1秒なら、chargeRateは0.5になります。つまり、ゲージは半分まで溜まっている状態です。

ただし、チャージ時間が最大値を超える可能性がある場合は、Mathf.Clamp01で0〜1の範囲に収めておくと安全です。

float chargeRate = Mathf.Clamp01(currentChargeTime / maxChargeTime);

この0〜1の値を、SliderImageに反映します。パワーゲージの考え方は、HPバーやロード進捗にも近いです。

  • 0:まったく溜まっていない
  • 0.5:半分まで溜まっている
  • 1:最大まで溜まっている

この「0〜1に変換する」という考え方を覚えておくと、UIゲージ系の実装が一気に扱いやすくなります。

Sliderでパワーゲージを作る

まずは、UIのSliderを使う方法です。横長のチャージバーを作りたい場合は、Sliderが分かりやすいです。

大まかな手順は次の通りです。

  1. Hierarchyで右クリックする
  2. UIからSliderを作成する
  3. SliderのMin Valueを0にする
  4. SliderのMax Valueを1にする
  5. スクリプトからslider.valueにチャージ割合を入れる

Sliderを使ったパワーゲージの例です。

using UnityEngine;
using UnityEngine.UI;

public class ChargeGaugeSlider : MonoBehaviour
{
    [SerializeField] private Slider chargeSlider;
    [SerializeField] private float maxChargeTime = 2f;

    private float chargeTime = 0f;
    private bool isCharging = false;

    private void Start()
    {
        chargeSlider.minValue = 0f;
        chargeSlider.maxValue = 1f;
        chargeSlider.value = 0f;
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            chargeTime = 0f;
            isCharging = true;
        }

        if (Input.GetMouseButton(0) && isCharging)
        {
            chargeTime += Time.deltaTime;
            chargeTime = Mathf.Min(chargeTime, maxChargeTime);

            UpdateGauge();
        }

        if (Input.GetMouseButtonUp(0) && isCharging)
        {
            isCharging = false;

            Debug.Log("チャージ割合: " + GetChargeRate());

            chargeTime = 0f;
            UpdateGauge();
        }
    }

    private float GetChargeRate()
    {
        return Mathf.Clamp01(chargeTime / maxChargeTime);
    }

    private void UpdateGauge()
    {
        chargeSlider.value = GetChargeRate();
    }
}

このコードでは、押している間だけchargeTimeを増やし、その割合をchargeSlider.valueに入れています。

離したあとにchargeTimeを0へ戻し、ゲージも0へ戻しています。ここを忘れると、攻撃後もゲージが満タンのまま残ってしまいます。

ImageのfillAmountでパワーゲージを作る

円形ゲージや、少し見た目を作り込みたいゲージでは、UIのImageにあるfillAmountを使う方法もあります。

fillAmountも0〜1の値で表示量を決めます。

  • 0:表示なし
  • 0.5:半分表示
  • 1:全部表示

Imageを使う場合は、InspectorでImage TypeをFilledに設定します。これを忘れると、fillAmountを変更しても見た目が変わりません。ここ、かなり引っかかりやすいです。

ImageのfillAmountを使う例です。

using UnityEngine;
using UnityEngine.UI;

public class ChargeGaugeImage : MonoBehaviour
{
    [SerializeField] private Image chargeImage;
    [SerializeField] private float maxChargeTime = 2f;

    private float chargeTime = 0f;
    private bool isCharging = false;

    private void Start()
    {
        chargeImage.fillAmount = 0f;
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            chargeTime = 0f;
            isCharging = true;
        }

        if (Input.GetMouseButton(0) && isCharging)
        {
            chargeTime += Time.deltaTime;
            chargeTime = Mathf.Min(chargeTime, maxChargeTime);

            UpdateGauge();
        }

        if (Input.GetMouseButtonUp(0) && isCharging)
        {
            isCharging = false;

            float chargeRate = GetChargeRate();
            Debug.Log("チャージ割合: " + chargeRate);

            chargeTime = 0f;
            UpdateGauge();
        }
    }

    private float GetChargeRate()
    {
        return Mathf.Clamp01(chargeTime / maxChargeTime);
    }

    private void UpdateGauge()
    {
        chargeImage.fillAmount = GetChargeRate();
    }
}

Sliderより少し設定は増えますが、円形のゲージや特殊な形のゲージを作りたい場合には便利です。

たとえば、魔法の詠唱ゲージ、必殺技ゲージ、クールタイム表示などはImage.fillAmountと相性が良いです。

Input Systemでゲージを更新する

Input Systemを使う場合も、ゲージの考え方は変わりません。押し始めた時刻を保存し、押している間に現在のチャージ割合を計算してUIへ反映します。

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class ChargeGaugeInputSystem : MonoBehaviour
{
    [SerializeField] private InputAction chargeAction;
    [SerializeField] private Slider chargeSlider;
    [SerializeField] private float maxChargeTime = 2f;

    private float chargeStartTime;
    private bool isCharging = false;

    private void OnEnable()
    {
        chargeAction.started += OnChargeStarted;
        chargeAction.canceled += OnChargeCanceled;
        chargeAction.Enable();
    }

    private void OnDisable()
    {
        chargeAction.started -= OnChargeStarted;
        chargeAction.canceled -= OnChargeCanceled;
        chargeAction.Disable();
    }

    private void Start()
    {
        chargeSlider.minValue = 0f;
        chargeSlider.maxValue = 1f;
        chargeSlider.value = 0f;
    }

    private void Update()
    {
        if (isCharging)
        {
            UpdateGauge();
        }
    }

    private void OnChargeStarted(InputAction.CallbackContext context)
    {
        chargeStartTime = Time.time;
        isCharging = true;
        chargeSlider.value = 0f;
    }

    private void OnChargeCanceled(InputAction.CallbackContext context)
    {
        if (!isCharging)
        {
            return;
        }

        float chargeRate = GetChargeRate();
        Debug.Log("離した瞬間のチャージ割合: " + chargeRate);

        isCharging = false;
        chargeSlider.value = 0f;
    }

    private float GetChargeRate()
    {
        float chargeTime = Time.time - chargeStartTime;
        return Mathf.Clamp01(chargeTime / maxChargeTime);
    }

    private void UpdateGauge()
    {
        chargeSlider.value = GetChargeRate();
    }
}

このコードでは、startedでチャージ開始、canceledでチャージ終了、Updateでゲージ更新をしています。

「Input SystemなのにUpdateを使っていいの?」と思うかもしれません。ゲージのように毎フレーム見た目を更新したい処理では、Updateを使うこともあります。大事なのは、入力の開始・終了判定と、UIの表示更新を混ぜすぎないことです。

Hold Interactionの進捗を使う場合

Input SystemのHold Interactionを使っている場合は、長押しの進捗を取得してゲージに反映する方法もあります。

Hold Interactionは、「一定時間押し続けたら完了」という判定に向いています。たとえば、ボタンを1秒押し続けたらスキル発動、扉を開ける、アイテムを拾う、といった操作です。

Holdの進捗を表示したい場合は、GetTimeoutCompletionPercentage()を使うと、タイムアウト完了までの割合を取得できます。

private void Update()
{
    if (isCharging)
    {
        chargeSlider.value = chargeAction.GetTimeoutCompletionPercentage();
    }
}

ただし、自分でチャージ時間を管理している場合は、chargeTime / maxChargeTimeで計算するほうが分かりやすいです。

使い分けるなら、次のように考えるとよいです。

作りたい処理ゲージの作り方
一定時間押したら成功Hold Interactionの進捗を使う
離した瞬間に溜め時間で威力を決める自分でチャージ時間を計算する
最大チャージ後も押しっぱなしにできる0〜1に丸めて表示する

ゲージ表示でよくある失敗

パワーゲージがうまく動かないときは、入力処理よりUI設定が原因になっていることも多いです。

  • SliderやImageがInspectorで割り当てられていない
  • SliderのMin ValueとMax Valueが想定と違う
  • Image TypeがFilledになっていない
  • チャージ終了後にゲージを0へ戻していない
  • 0〜1ではなく、秒数をそのまま入れている

特に多いのは、秒数をそのままUIに入れてしまうパターンです。最大チャージ時間が2秒のとき、Sliderの最大値が1なのに2を入れても、意図した見た目にはなりません。

ゲージ表示では、まず「今の値を0〜1に変換できているか」を確認しましょう。ここができていれば、SliderでもImageでも応用しやすくなります。

ゲージの見た目を強化する

最初はシンプルなSliderで十分ですが、ゲームらしい見た目にしたい場合は、ゲージ演出用のアセットを使うのも選択肢です。

特に、チャージ攻撃や必殺技ゲージはプレイヤーが何度も見るUIなので、見た目の気持ちよさが操作感にもつながります。自作に時間をかけすぎるより、使えるものを活用してゲーム本体の調整に時間を回すのも良い判断です。

Procedural Progress Bars
✅アセットストアでチェックする

Progress Bar System
✅アセットストアでチェックする




Unity入力処理のよくある誤解と注意点

長押し、チャージ攻撃、ドラッグ発射、連射、パワーゲージは、それぞれ単体ならそこまで難しくありません。

ただ、実際のゲームではこれらを組み合わせることが多いです。すると、「押した瞬間」「押している間」「離した瞬間」の役割が混ざって、思った通りに動かなくなることがあります。

ここでは、初心者がつまずきやすい誤解を整理しておきます。バグが出たときのチェックリストとしても使えます。

長押しはクリック処理の延長ではない

クリック処理は、一瞬の入力を見る処理です。たとえば、押した瞬間にジャンプする、クリックした瞬間に弾を出す、といった使い方です。

一方で、長押し処理は「押している時間」を扱います。押されたかどうかだけでは足りません。

たとえば、次のような処理はクリックだけでは作りにくいです。

  • 1秒以上押したら強攻撃にする
  • 押している間だけゲージを増やす
  • 離した瞬間に溜めた力を使う
  • 押している間だけ連射する

この場合は、押した瞬間、押している間、離した瞬間を分けて考えます。

よくある失敗は、GetMouseButtonDownだけで長押しを作ろうとすることです。GetMouseButtonDownは押した瞬間だけ反応するので、押し続けている時間は分かりません。

旧Inputなら、押している間はGetMouseButton、離した瞬間はGetMouseButtonUpを使います。Input Systemなら、押し始めはstarted、離したタイミングはcanceledを使うと整理しやすいです。

Holdを使えばすべて解決するわけではない

Input Systemには、Hold Interactionという長押し判定があります。一定時間押し続けたかを判定したい場合には便利です。

ただし、Holdを使えば長押し系の処理が全部きれいに解決する、というわけではありません。

たとえば、次のような処理ではHoldが向いています。

  • 1秒押したら扉を開ける
  • 一定時間押したらアイテムを拾う
  • 長押し完了でスキルを発動する

一方で、次のような処理では、自分でチャージ時間を計算したほうが分かりやすいことがあります。

  • 0.5秒なら弱攻撃、1.5秒なら強攻撃にする
  • 離した瞬間のチャージ量で威力を決める
  • 最大チャージ後もボタンを押しっぱなしにできる
  • チャージ量を独自の計算式で攻撃力に変換する

Holdは「条件を満たしたか」を見るのが得意です。チャージ攻撃のように「どれくらい溜めたか」を細かく使いたい場合は、開始時刻と終了時刻の差を取る方法も候補になります。

つまり、Holdは便利な道具ですが、万能魔法ではありません。魔法使いでもMP管理は必要です。

ドラッグ距離だけ見ればよいわけではない

ドラッグ発射では、ドラッグした距離だけでなく方向も大切です。

距離だけを見ていると、「強さ」は決められても、「どちらへ飛ばすか」が決まりません。

ドラッグ操作では、開始位置と終了位置の差からベクトルを作ります。ベクトルには、距離と方向の両方が含まれています。

  • 距離:どれくらい強く飛ばすか
  • 方向:どちらへ飛ばすか

発射方向が逆になる場合は、差分の取り方を確認してください。

Vector3 dragVector = endPosition - startPosition;

これは、ドラッグした方向へ飛ばす考え方です。

Vector3 dragVector = startPosition - endPosition;

これは、引っ張った反対方向へ飛ばす考え方です。弓やパチンコのような操作では、こちらのほうが自然に感じることがあります。

どちらが正しいかは、ゲームの操作感によって変わります。コードの正誤だけでなく、「プレイヤーがどう動かしたいと思うか」を基準に決めるのがおすすめです。

連射処理は毎フレーム撃てばよいわけではない

押している間に弾を撃ちたい場合でも、毎フレーム発射するのは避けたほうがよいです。

Unityは多くの場合、1秒間に何十回もUpdateを実行します。そのたびに弾を生成すると、想定より大量の弾が出ます。

連射処理では、必ず発射間隔を決めます。

[SerializeField] private float fireInterval = 0.2f;

このように変数にしておくと、Inspectorから発射速度を調整できます。

連射処理で確認したいポイントは次の通りです。

  • 発射間隔を決めているか
  • 押した瞬間に連射を開始しているか
  • 離した瞬間に連射を止めているか
  • コルーチンが二重起動していないか
  • 弾を大量に出す場合、後でプール化できる構造か

特にコルーチンを使う場合は、開始処理だけでなく停止処理もセットで書きます。StartCoroutineだけ書いてStopCoroutineを忘れると、押していないのに処理が続く原因になります。

UIボタン外で離したときの挙動を忘れやすい

マウスやキーボード入力では問題なく動いても、UIボタンの長押しで挙動が崩れることがあります。

たとえば、スマホ風のUIボタンを押して、ボタンの外まで指を動かしてから離した場合です。このとき、離した処理が想定通りに呼ばれないと、押下中フラグが残ることがあります。

押下中フラグが残ると、次のような不具合につながります。

  • ボタンを離したのにゲージが増え続ける
  • 攻撃が止まらない
  • 次の入力で前回のチャージが残る
  • 押していないのに連射が続く

UIボタンで長押しを作る場合は、押したときだけでなく、離したとき、キャンセルされたとき、オブジェクトが無効になったときにも状態を戻す意識が必要です。

private void ResetInputState()
{
    isCharging = false;
    chargeTime = 0f;
}

このように、入力状態をまとめて初期化するメソッドを用意しておくと安心です。

入力処理とUI表示を混ぜすぎない

チャージ攻撃とパワーゲージを同時に作ると、入力判定とUI更新を同じ場所に詰め込みがちです。

最初のサンプルではそれでも問題ありませんが、処理が増えてきたら役割を分けたほうが読みやすくなります。

  • 入力処理:押した、押している、離したを判定する
  • 状態管理:チャージ時間や発射間隔を管理する
  • UI表示:ゲージやアイコンを更新する
  • 実行処理:攻撃、発射、エフェクト再生を行う

この分け方をしておくと、「入力はできているけどゲージが動かない」「ゲージは動くけど攻撃が出ない」といった問題を切り分けやすくなります。

ゲーム制作では、いきなり完璧な設計を目指さなくても大丈夫です。ただ、処理が増えてきたタイミングで少しずつ役割を分けると、後から直しやすいコードになります。

正常な動きと異常な動きで確認する

入力処理で迷ったときは、正常な動きと異常な動きを書き出してみるのがおすすめです。

処理正常な動き見直すべき動き
長押し押している間だけ時間が増える離しても時間が増え続ける
チャージ攻撃離した瞬間に威力が決まる押した瞬間に攻撃が出る
ドラッグ発射距離と方向に応じて飛ぶ逆方向へ飛ぶ、または飛ばない
連射一定間隔で発射される毎フレーム発射される
パワーゲージ0〜1の割合で増減する満タンのまま戻らない

バグを直すときは、コードを全部眺めるより、「どのタイミングの処理がおかしいのか」を先に決めると早いです。

押した瞬間がおかしいのか、押している間がおかしいのか、離した瞬間がおかしいのか。そこが分かるだけで、見るべきコードの範囲がかなり狭くなります。




まとめ:Unityの入力操作はタイミングで分ける

Unityで長押し、チャージ攻撃、ドラッグ発射、連射、パワーゲージを作るときは、まず「どのタイミングで何をしたいのか」を分けることが大切です。

入力処理は、コードだけを見ると複雑に感じやすいですが、基本は次の3つです。

  • 押した瞬間
  • 押している間
  • 離した瞬間

長押し処理では、押している時間を計測します。チャージ攻撃では、離した瞬間にその時間を使って攻撃内容を決めます。ドラッグ発射では、押した位置と離した位置の差から、距離と方向を計算します。

連射処理では、押しているかどうかだけでなく、発射間隔を管理する必要があります。パワーゲージでは、現在のチャージ量を最大値で割って、0〜1の割合として表示すると扱いやすくなります。

作りたい操作見るべきポイント主な使い道
クリック押した瞬間通常攻撃、決定、ジャンプ
長押し押している時間溜め動作、ゲージ増加
チャージ攻撃離した瞬間の溜め時間弱攻撃・強攻撃の分岐
ドラッグ発射開始位置と終了位置の差弓、パチンコ、引っ張り操作
連射押下状態と発射間隔マシンガン、連続ショット
パワーゲージ現在値 ÷ 最大値チャージ量や進捗の表示

旧Inputは、Input.GetMouseButtonDownInput.GetMouseButtonInput.GetMouseButtonUpを使って流れを追いやすいので、学習用や小さなサンプルに向いています。

Input Systemは、startedperformedcanceledなどのイベントで入力を分けられるため、ゲームパッドやスマホ操作など、複数デバイスに対応したいときに便利です。

ただし、どちらを使う場合でも、操作設計の考え方は大きく変わりません。APIの名前を覚える前に、「押したときに何をするか」「押している間に何をするか」「離したときに何をするか」を先に決めておくと、コードに落とし込みやすくなります。

私の感覚では、入力処理でつまずく原因の多くは、コードの難しさよりも「タイミングの混在」です。押した瞬間にやる処理と、離した瞬間にやる処理が混ざると、どんどん分かりにくくなります。

まずはログだけで動きを確認し、次にゲージや弾の発射をつなげ、最後にエフェクトや音を足していく。この順番で作ると、バグの原因を見つけやすいです。

長押しやドラッグ操作は、ゲームの手触りに直結する大事な部分です。少しずつ調整しながら、「押して気持ちいい」「離して気持ちいい」操作を作っていきましょう。

よくある質問(FAQ)

Q
初心者は旧InputとInput Systemのどちらから始めるべき?
A

まず入力処理の考え方をつかみたいなら、旧Inputから始めても大丈夫です。

旧Inputは、Input.GetMouseButtonDownInput.GetMouseButtonInput.GetMouseButtonUpのように、「押した瞬間」「押している間」「離した瞬間」をコード上で追いやすいです。

たとえば、マウスだけで長押し攻撃を試したい場合や、クリックで弾を撃つ小さなサンプルを作りたい場合は、旧Inputのほうが早く形にできます。

一方で、今後ゲームパッド、スマホ、キーコンフィグまで対応したいなら、Input Systemに慣れておくと拡張しやすくなります。

  • 学習用・小規模サンプル:旧Input
  • マウスとキーボードだけ:旧Inputでも十分
  • ゲームパッド対応:Input Systemがおすすめ
  • スマホや複数デバイス対応:Input Systemがおすすめ
  • キーコンフィグ対応:Input Systemがおすすめ

私なら、最初は旧Inputで「押す・押している間・離す」の流れを理解してから、同じ処理をInput Systemで書き直してみます。すると、APIの違いよりも操作設計の共通点が見えやすくなります。

Q
チャージ攻撃と連射は同じボタンで作れる?
A

同じボタンで作ることはできます。ただし、条件をかなり丁寧に分ける必要があります。

たとえば、同じ攻撃ボタンに次の処理を全部入れたいとします。

  • 短く押したら通常攻撃
  • 押しっぱなしなら連射
  • 長く押して離したらチャージ攻撃

このような操作は便利に見えますが、処理のタイミングがぶつかりやすいです。

たとえば、押しっぱなしで連射を始めたあと、離した瞬間にチャージ攻撃も出てしまうかもしれません。逆に、チャージ攻撃を優先しすぎると、連射が始まらないこともあります。

同じボタンに複数の役割を持たせる場合は、先にルールを決めておきましょう。

  • 0.2秒未満で離したら通常攻撃
  • 0.2秒以上押したらチャージ開始
  • 連射武器のときはチャージ攻撃を無効にする
  • チャージ武器のときは連射を無効にする

初心者のうちは、1つのボタンに全部詰め込まないほうが安全です。まずは「通常攻撃だけ」「チャージ攻撃だけ」「連射だけ」のように分けて作り、動きが安定してから組み合わせると失敗しにくいです。

ゲームとして自然にするなら、武器の種類ごとに入力の意味を変える方法もあります。マシンガンなら押しっぱなしで連射、弓なら長押しでチャージ、パチンコならドラッグ発射、というように分けるとプレイヤーも迷いにくくなります。

Q
スマホのタップやドラッグにも同じ考え方を使える?
A

基本の考え方は使えます。

スマホのタップやドラッグでも、「押した瞬間」「押している間」「離した瞬間」に分けて考える点は同じです。

  • タップ開始:チャージ開始、ドラッグ開始位置の保存
  • タップ中:ゲージ更新、現在位置の確認
  • タップ終了:攻撃発動、ドラッグ量の確定

ただし、スマホ操作ではマウス操作よりも注意点が増えます。

  • 指が少し動いただけでドラッグ扱いになる
  • UIボタンの上をタップしたときの処理が必要になる
  • 複数の指で触れたときの扱いを決める必要がある
  • 画面座標とワールド座標の変換が必要になることがある

特に、スマホでは「少し指がズレただけ」の入力をどう扱うかが大事です。短すぎるドラッグは無視する、一定距離以上動いたらドラッグ扱いにする、というしきい値を用意すると操作感が安定します。

if (dragVector.magnitude < minDragDistance)
{
    return;
}

また、UIボタンとゲーム画面のタップ判定が重なると、ボタンを押したつもりなのにキャラクターも動く、といった不具合が起きることがあります。

まずはマウス1つ、または指1本の操作で正しく動く形を作りましょう。そのあとで、マルチタッチやUIとの競合対策を追加すると、原因を切り分けやすくなります。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク