【Unity】オブジェクトに複雑な動作をさせるUIボタンの作成

Unity

今回の記事では、Unityで簡単にカスタマイズ可能な動きをオブジェクトに適用する方法を紹介します。特に、UIボタンを使用して、オブジェクトにさまざまな動きを割り当てる手法に焦点を当てています。この方法を用いることで、車のモデルなどのオブジェクトに対して、移動、回転、スケール変更といった動きを実現することができます。

最終的に作成したいものはこちら↓

オブジェクトやCanvasの準備

まずは、新しいUnityプロジェクトを開いたら、ヒエラルキーウィンドウに今回動かす対象となるオブジェクトと背景、UIボタンを配置するためのCanvasを追加していきます。

今回は下記画像のような車やガレージの3Dモデルを、UnityのアセットストアやCGtraderからダウンロードし、Unityのプロジェクトに追加しています。

ポイント

今回は車のドアを開閉する動きを作成したかったので、各ドアが車両本体(body)と別パーツとなっているものを選んで使用しています。車両本体とドアパーツが分かれていないと、ドアだけを動かしたいとなった時にとても難しくなります。

次に、UIボタンを配置するためのCanvasや各種UIボタンをヒエラルキーウィンドウに追加していきます。詳しい追加方法については下記の記事を参考にしてみてください。

今回は、車のドアオブジェクトを開閉させる、車自体のサイズを大きくさせる、車自体を回転、移動させるということをしたかったため、UIボタンとしては下記画像の通り5つ作成しています。

ここまでで、動作別のUIボタンの作成と、そのボタンによって動かしたいオブジェクト(※今回は車両本体とドア)とその背景の準備が完了です。

スクリプトの作成とアタッチ

では次に、カメラ移動やボタン操作などのユーザー操作を一括で管理するために「UIManager」という空のオブジェクトをヒエラルキーウィンドウに追加しておきます。

ここで作成した「UIManager」にアタッチするための具体的なスクリプトは下記の二つ↓

using UnityEngine;
using UnityEngine.EventSystems; // EventSystemの名前空間を追加
using System.Collections.Generic; // リストを使用するため

public class CameraController : MonoBehaviour
{
    public Camera controlledCamera; // 制御するカメラ
    public GameObject targetObject; // 中心にするオブジェクト
    public float zoomSpeed = 10f; // ズームの速度
    public float minZoomDistance = 10f; // 最小ズーム距離
    public float maxZoomDistance = 100f; // 最大ズーム距離
    public float rotationSpeed = 5f; // 回転の速度
    public float minXRotation = 0f; // X軸方向の最小回転角度
    public float maxXRotation = 45f; // X軸方向の最大回転角度

    private float currentZoomDistance;
    private Vector3 originalCameraPosition;
    private Quaternion originalCameraRotation;
    private float currentXRotation = 0f; // 現在のX軸回転角度

    void Start()
    {
        originalCameraPosition = controlledCamera.transform.position;
        originalCameraRotation = controlledCamera.transform.rotation;
        currentZoomDistance = Vector3.Distance(controlledCamera.transform.position, targetObject.transform.position);
        currentXRotation = controlledCamera.transform.eulerAngles.x;
    }

    void Update()
    {
        HandleZoom();
        if (!IsPointerOverUIObject()) // UIオブジェクト上にない場合のみ実行
        {
            HandleRotationAroundObject();
        }
    }

    void HandleZoom()
    {
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        currentZoomDistance = Mathf.Clamp(currentZoomDistance - scroll * zoomSpeed, minZoomDistance, maxZoomDistance);
        controlledCamera.transform.position = targetObject.transform.position - controlledCamera.transform.forward * currentZoomDistance;
    }

    void HandleRotationAroundObject()
    {
        if (Input.GetMouseButton(0)) // 左クリックで回転
        {
            float horizontalRotation = Input.GetAxis("Mouse X") * rotationSpeed;
            float verticalRotation = -Input.GetAxis("Mouse Y") * rotationSpeed;

            // X軸回転を制限
            currentXRotation = Mathf.Clamp(currentXRotation + verticalRotation, minXRotation, maxXRotation);
            verticalRotation = currentXRotation - controlledCamera.transform.eulerAngles.x;

            // カメラの回転を制御
            controlledCamera.transform.RotateAround(targetObject.transform.position, Vector3.up, horizontalRotation);
            controlledCamera.transform.RotateAround(targetObject.transform.position, controlledCamera.transform.right, verticalRotation);
        }
    }

    bool IsPointerOverUIObject()
    {
        PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
        eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
        return results.Count > 0;
    }
}
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;

public class UIManagerAdvanced : MonoBehaviour
{
    // ボタンとそれに関連するアクションのリストを定義
    public List<ButtonAction> buttonActions;

    // ボタンアクションの設定
    [System.Serializable]
    public class ButtonAction
    {
        public Button button; // ボタンの参照
        public GameObject targetObject; // 操作するオブジェクトの参照
        public List<ActionSetting> actions; // 実行するアクションのリスト
    }

    // アクションの設定
    [System.Serializable]
    public class ActionSetting
    {
        public MoveAction moveAction; // 移動アクションの設定
        public RotateAction rotateAction; // 回転アクションの設定
        public ScaleAction scaleAction; // スケール変更アクションの設定

        public float delay; // アクション開始までの遅延時間
    }

    // 移動アクションの設定
    [System.Serializable]
    public class MoveAction
    {
        public Vector3 targetValue; // 移動の目標値
        public float duration; // 移動の所要時間
    }

    // 回転アクションの設定
    [System.Serializable]
    public class RotateAction
    {
        public Vector3 targetValue; // 回転の目標値
        public float duration; // 回転の所要時間
    }

    // スケール変更アクションの設定
    [System.Serializable]
    public class ScaleAction
    {
        public float scaleMultiplier; // スケールの倍率
        public float duration; // スケール変更の所要時間
    }

    private void Start()
    {
        // 各ボタンにアクションを割り当てる
        foreach (var buttonAction in buttonActions)
        {
            buttonAction.button.onClick.AddListener(() => StartCoroutine(PerformActions(buttonAction.targetObject, buttonAction.actions)));
        }
    }

    // 実行するアクションのコルーチン
    private IEnumerator PerformActions(GameObject targetObject, List<ActionSetting> actions)
    {
        foreach (var action in actions)
        {
            // アクション開始前の遅延
            yield return new WaitForSeconds(action.delay);

            // 移動アクションの実行
            if (action.moveAction != null)
            {
                StartCoroutine(MoveOverTime(targetObject, action.moveAction.targetValue, action.moveAction.duration));
            }

            // 回転アクションの実行
            if (action.rotateAction != null)
            {
                StartCoroutine(RotateOverTime(targetObject, action.rotateAction.targetValue, action.rotateAction.duration));
            }

            // スケール変更アクションの実行
            if (action.scaleAction != null)
            {
                StartCoroutine(ScaleOverTime(targetObject, action.scaleAction.scaleMultiplier, action.scaleAction.duration));
            }
        }
    }

    // 移動アクションのコルーチン
    private IEnumerator MoveOverTime(GameObject targetObject, Vector3 target, float duration)
    {
        Vector3 startPosition = targetObject.transform.position;
        Vector3 endPosition = startPosition + target;
        for (float t = 0; t < duration; t += Time.deltaTime)
        {
            targetObject.transform.position = Vector3.Lerp(startPosition, endPosition, t / duration);
            yield return null;
        }
        targetObject.transform.position = endPosition;
    }

    // 回転アクションのコルーチン
    private IEnumerator RotateOverTime(GameObject targetObject, Vector3 target, float duration)
    {
        Quaternion startRotation = targetObject.transform.rotation;
        Quaternion endRotation = targetObject.transform.rotation * Quaternion.Euler(target);
        for (float t = 0; t < duration; t += Time.deltaTime)
        {
            targetObject.transform.rotation = Quaternion.Lerp(startRotation, endRotation, t / duration);
            yield return null;
        }
        targetObject.transform.rotation = endRotation;
    }

    // スケール変更アクションのコルーチン
    private IEnumerator ScaleOverTime(GameObject targetObject, float multiplier, float duration)
    {
        Vector3 startScale = targetObject.transform.localScale;
        Vector3 endScale = startScale * multiplier;
        for (float t = 0; t < duration; t += Time.deltaTime)
        {
            targetObject.transform.localScale = Vector3.Lerp(startScale, endScale, t / duration);
            yield return null;
        }
        targetObject.transform.localScale = endScale;
    }
}

作成した2つのスクリプトを先ほど作成した「UIManager」という名前のオブジェクトにアタッチしましょう。

インスペクタウィンドウの設定

次に先ほど作成した「UIManager」のインスペクタウィンドウを設定していきます。

まずは、一つ目のスクリプト「Camera Controller」のそれぞれのパラメーターは下記のようになっています。それぞれのパラメーターを少しずつ調整することで、カメラの動かせる範囲を制御できます。

次に二つ目のスクリプト「UI Manager Advanced」で設定できるそれぞれのパラメーターは下記のようになっています。

これらの設定を行うと、「Button」で設定したUIボタンを押すことで、「Target Object」で指定したオブジェクトが、「Actions」の動作を上から順番に実行されるようになります。

結果の確認

最後にシーンを実行して動きを確認してみます。今回のUIボタンの操作により、車のドアオブジェクトが開閉することや、車自体が前に進むことを確認できました。

まとめ

今回のブログ記事では、UnityにおけるUIボタンを使用したオブジェクトの複雑な動きの制御方法を紹介しました。この方法を利用することで、ゲームやアプリケーション内でオブジェクトに動的なアニメーションを簡単に適用することができます。UIボタンのカスタマイズにより、さまざまな動きを簡単に操作し、ユーザー体験を向上させることが可能です。

コメント