UnityでUIを作っていると、つい「とりあえず全部 Update に書いて動けばOK!」となりがちですよね。
例えば、マウスホバーの判定・ボタンの色変更・クリック処理・SE再生などを、1つの巨大なスクリプトに押し込んでしまうパターンです。

このやり方は最初は楽ですが、後から

  • ボタンの見た目だけ変えたいのに、ロジックまで巻き込んで修正が必要
  • 別の画面でも同じようなホバー演出を使いたいのに、コピペ地獄になる
  • 「どの処理がどこで動いているのか」追いにくくてデバッグが辛い

といった問題を生みやすいです。

そこでこの記事では、「ホバーしたときに少し拡大して強調する」という機能だけを切り出した、
小さなコンポーネント HoverScale (ホバー拡大) を作ってみましょう。
ボタンでもアイコンでも、対象のゲームオブジェクトにアタッチするだけで、ホバー拡大演出を簡単に再利用できるようにします。

【Unity】ホバーでふわっと拡大!「HoverScale」コンポーネント

今回の HoverScale は、次のような特徴を持つコンポーネントです。

  • マウスカーソルが乗ったときに、親の Transform を少しだけ拡大
  • カーソルが外れたら、元のスケールにスムーズに戻す
  • 拡大倍率・アニメーション速度をインスペクターから調整可能
  • UIボタンだけでなく、アイコンや任意のオブジェクトにも使える

「ホバーしたら拡大する」という見た目の責務だけをこのコンポーネントに任せることで、
クリック処理やゲームロジックとはきれいに分離できます。

フルコード:HoverScale.cs


using UnityEngine;
using UnityEngine.EventSystems;

namespace HoverEffects
{
    /// <summary>
    /// マウスホバー時に対象オブジェクトをふわっと拡大するコンポーネント
    /// - IPointerEnterHandler / IPointerExitHandler を利用
    /// - UI(Button, Image, etc...) はもちろん、EventSystem + Raycaster があれば3Dオブジェクトにも使用可能
    /// </summary>
    [DisallowMultipleComponent]
    public class HoverScale : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
    {
        // 拡大対象(null の場合はこのコンポーネントが付いているオブジェクト自身を対象とする)
        [SerializeField]
        private Transform targetTransform;

        // ホバー時の拡大倍率(1.1f なら 10% 拡大)
        [SerializeField, Tooltip("ホバー時の拡大倍率(1.1 なら 10% 拡大)")]
        private float hoverScaleMultiplier = 1.1f;

        // スケールの補間速度(大きいほど素早く変化)
        [SerializeField, Tooltip("スケールの補間速度(大きいほど素早く変化)")]
        private float scaleLerpSpeed = 12f;

        // ホバー中かどうかのフラグ
        private bool _isPointerOver;

        // 元のスケールを保存
        private Vector3 _initialScale;

        // 現在のスケール(補間用)
        private Vector3 _currentScale;

        private void Awake()
        {
            // 対象が指定されていなければ、自身の Transform を対象にする
            if (targetTransform == null)
            {
                targetTransform = transform;
            }

            // 初期スケールを保存
            _initialScale = targetTransform.localScale;
            _currentScale = _initialScale;
        }

        private void OnEnable()
        {
            // 再有効化時にスケールをリセットしておくと安心
            if (targetTransform != null)
            {
                targetTransform.localScale = _initialScale;
                _currentScale = _initialScale;
            }

            _isPointerOver = false;
        }

        private void Update()
        {
            if (targetTransform == null)
            {
                return;
            }

            // 目標スケールを決定
            Vector3 targetScale = _isPointerOver
                ? _initialScale * hoverScaleMultiplier
                : _initialScale;

            // スムーズに補間(Lerp)
            _currentScale = Vector3.Lerp(
                _currentScale,
                targetScale,
                Time.unscaledDeltaTime * scaleLerpSpeed
            );

            targetTransform.localScale = _currentScale;
        }

        /// <summary>
        /// マウスカーソルが UI 要素に乗ったときに呼ばれる
        /// </summary>
        public void OnPointerEnter(PointerEventData eventData)
        {
            _isPointerOver = true;
        }

        /// <summary>
        /// マウスカーソルが UI 要素から外れたときに呼ばれる
        /// </summary>
        public void OnPointerExit(PointerEventData eventData)
        {
            _isPointerOver = false;
        }

        /// <summary>
        /// 実行中に倍率を変更したいケース向けの API(任意で使用)
        /// </summary>
        /// <param name="multiplier">新しい拡大倍率</param>
        public void SetHoverScaleMultiplier(float multiplier)
        {
            hoverScaleMultiplier = Mathf.Max(0f, multiplier);
        }

        /// <summary>
        /// 実行中に補間速度を変更したいケース向けの API(任意で使用)
        /// </summary>
        /// <param name="speed">新しい補間速度</param>
        public void SetLerpSpeed(float speed)
        {
            scaleLerpSpeed = Mathf.Max(0f, speed);
        }
    }
}

使い方の手順

ここでは、典型的な「メニュー画面のボタン」と「インベントリアイコン」での使用例をベースに手順を説明します。

  1. ① 必要な UI 環境を用意する
    • シーンに EventSystem が存在することを確認(なければ GameObject > UI > Event System で追加)
    • Canvas 上に ButtonImage などの UI 要素を配置する
  2. ② スクリプトを作成してアタッチ
    • プロジェクトビューで HoverScale.cs を作成し、上記のコードをコピペ
    • メニューの StartButton や、インベントリのアイコンオブジェクトなど「拡大させたいオブジェクト」に HoverScale をアタッチ

    例:

    • StartButton(Button コンポーネント付きの GameObject)
    • InventoryItemIcon(Image コンポーネント付きの GameObject)
  3. ③ インスペクターでパラメータを調整
    • Target Transform
      通常は空欄のままで OK(自分自身が拡大対象になります)。
      もし「ボタンの中のアイコンだけ拡大したい」などがあれば、その子オブジェクトの Transform をドラッグ&ドロップで指定します。
    • Hover Scale Multiplier
      ホバー時の拡大倍率。1.05 ~ 1.2 くらいが使いやすいです。
      例:1.1 にすると 10% 拡大します。
    • Scale Lerp Speed
      スケールが変化する速さ。8 ~ 16 くらいが自然に感じやすいです。
      値を大きくすると、ピタッと素早く拡大・縮小します。
  4. ④ 実際に動作を確認する
    • Play モードで再生し、対象のボタンやアイコンにマウスカーソルを乗せてみる
    • ふわっと拡大して、カーソルを外すと元に戻れば成功です
    • 複数のボタンに同じ HoverScale をアタッチしても、コードは 1 つなので保守も簡単です

これだけで、プレイヤーにとって分かりやすく、クリックしたくなる UI 演出を簡単に付けられます。

メリットと応用

HoverScale のように「ホバー時の拡大」という見た目の責務だけに特化したコンポーネントを用意しておくと、次のようなメリットがあります。

  • プレハブ管理が楽になる
    ボタン用プレハブに HoverScale を入れておけば、どのシーンに配置しても同じホバー演出が自動で効くようになります。
    振る舞いを変えたくなったら、このコンポーネントだけを修正すれば全ボタンに反映されます。
  • レベルデザイン/UIデザインの試行錯誤がしやすい
    拡大倍率やスピードをインスペクターから調整できるので、デザイナーやレベルデザイナーがシーン上で直接「気持ちいい値」を探れます。
    値を変えても他のロジックには一切影響しないため、安心して試行錯誤できます。
  • ロジックと演出の分離
    クリック処理などは Button.onClick や別コンポーネントに任せて、
    見た目の演出だけを HoverScale に担当させることで、各コンポーネントの責務が明確になります。
    結果として、巨大な God クラスを避けられ、保守性・再利用性が向上します。
  • UI 以外にも応用可能
    PhysicsRaycasterGraphicRaycaster を使えば、3D オブジェクトやワールド空間 UI にも同じコンポーネントを使えます。
    例えば、ワールドマップ上の建物アイコンに付けて「選択候補」を分かりやすくする、といった使い方もできます。

改造案:ホバー中だけ軽く回転させる

「拡大だけだと少し物足りないな」という場合は、軽い回転を加えるのもアリですね。
HoverScale に以下のようなメソッドを追加して、Update から呼び出す形に改造してみましょう。


private void ApplyHoverRotation()
{
    if (!_isPointerOver || targetTransform == null)
    {
        return;
    }

    // ホバー中だけ、Y軸にゆっくり回転を加える(UI なら Z 軸回転でもOK)
    float rotationSpeed = 10f; // 1秒あたり10度回転
    targetTransform.Rotate(0f, 0f, rotationSpeed * Time.unscaledDeltaTime);
}

このように、小さなコンポーネントをベースに少しずつ演出を足していくと、
プロジェクト全体が「再利用しやすい小さい部品の集合」になっていきます。
ぜひ自分のプロジェクト用にカスタマイズして、気持ちいいホバー演出を育てていきましょう。