LoginSignup
5
1

More than 1 year has passed since last update.

【Unity】前後移動はボタン、横移動はスマホの傾きでtoioを動かす

Posted at

本記事について

toio SDKのチュートリアルを一通りこなした後まず最初に作るべきものは、トイオコレクションに付属しているフリームーブのように自由に移動できるものが難易度がちょうど良いのではないかと考えた。

そこで以下のようなコントローラーを作成した。

スクリーンショット_2022-12-18_11_51_27.png
output.gif

本記事ではこの機能の実装方法を説明する。

環境

mac os 12.5 silicon
Unity 2021.3.7f1
Input System 1.3.0
iphone
toio SDK for Unity v1.5.1

本記事の対象者

Unityの基本的なことができる
toio SDKのチュートリアルができる
ビルドができる
Input Systemは使ったことがなくても大丈夫

コンセプト

スクリーンショット 2022-12-18 13.44.51.png

本記事のコードでは3つのクラスを定義している。より複雑なことをするならもっと細かく役割を分けた方がいいと思うが、シンプルさを重視した設計。

手順

toio SDKのインストール等は省略

Input Systemのインストール

Package Managerからインストール
スクリーンショット_2022-12-18_14_03_17.png

Scene上のEvent Systemのconvert。スクショはないが、convertのようなボタンが表示されてるはずなので押す。

スクリーンショット 2022-12-18 14.07.09.png
こうなってればおそらく大丈夫

Project Settings > Player > Other Settings > Configuration

にあるActive Input Handling をBothにして古いInput Managerが動くようにする。

スクリーンショット 2022-12-18 14.09.44.png

toio SDKをそのまま使う場合にはSimulatorが古いInput Managerを使用しているため。

ToioScene作成

スクリーンショット 2022-12-18 14.15.07.png

Canvas以下は前後用のボタンとDebug用のtextを置いているだけ。

ToioScene

記事にするにあたって読みやすくするために実際に動かしたコードから変更している。

ToioScene.cs
...
namespace Hoge
{
    public class ToioScene : MonoBehaviour
    {
        public ConnectType connectType;
        CubeManager cm;
        [SerializeField] FreeRunCubeController freeRunCubeController;

        void Start
        {
            if (GravitySensor.current != null)
            {
                InputSystem.EnableDevice(GravitySensor.current);
            }

            InitAsync().Forget();
        }

        async UniTask InitAsync()
        {
#if UNITY_EDITOR
            await SimulationCubeScene.LoadAsync();
#endif
            cm = new CubeManager(connectType);
            var cube = await cm.SingleConnect();
            var freeRunCube = new FreeRunCube(cm, cube);
            freeRunCubeController.Activate(freeRunCube);
        }
    }
}

動きをSimulatorで確認するためにUNITY_EDITORの場合にのみ別シーンでSimulatorを読み込んでいる。

FreeRunCubeController

主に入力に対するハンドラを記載。

FreeRunCubeController.cs
...
namespace Hoge.Toio
{
    public class FreeRunCubeController : MonoBehaviour
    {
        [SerializeField] TextMeshProUGUI sensorLabel;
        FreeRunCube freeRunCube = null;
        public void Activate(FreeRunCube freeRunCube)
        {
            this.freeRunCube = freeRunCube;
        }

        public void OnMoveForwardButtonDown()
        {
            if (freeRunCube == null)
            {
                return;
            }

            freeRunCube.StartMoveForward();
        }

        public void OnMoveForwardButtonUp()
        {
            if (freeRunCube == null)
            {
                return;
            }
            freeRunCube.EndMoveForward();
        }

        public void OnMoveBackButtonDown()
        {
            if (freeRunCube == null)
            {
                return;
            }
            freeRunCube.StartMoveBack();
        }

        public void OnMoveBackButtonUp()
        {
            if (freeRunCube == null)
            {
                return;
            }
            freeRunCube.EndMoveBack();
        }

        void Update()
        {
            var gravitySensor = GravitySensor.current;
            if (gravitySensor != null)
            {
                var enabled = gravitySensor.enabled;
                Vector3 gravity = gravitySensor.gravity.ReadValue();
                sensorLabel.text = $"enabled: {enabled}\n" +
                    $"Gravity\nX={gravity.x:#0.00} Y={gravity.y:#0.00} Z={gravity.z:#0.00}";

                freeRunCube?.SetHandleRotation(gravity.x);
            }else
            {
                freeRunCube?.SetHandleRotation(0);
            }

            freeRunCube?.OnUpdate();
        }
    }
}

傾きの取得方法については別記事にしたのでそちらを参考にどうぞ。

複雑なことをやりたい場合にはこのクラスをコマンドパターン等で書き換えるのが良いと思う。

スクリーンショット 2022-12-18 14.52.23.png

ハンドラはButtonにEventTriggerをつけて呼び出している。

FreeRunCube

FreeRunCube.cs
...
namespace Hoge.Toio
{
    public class FreeRunCube
    {
        public enum MoveType
        {
            Forward,
            Idle,
            Back
        }

        CubeManager cm;
        Cube cube;
        bool isForward = false;
        bool isBack = false;
        MoveType moveType = MoveType.Idle;

        public float forwardSpeed = 50f;
        public float backSpeed = 30f;
        public float forwardRotateBaseSpeed = 10f;
        public float backRotateBaseSpeed = 10f;
        public float rotateWeight = 1.0f;
        public float handleRotation = 0f;

        public FreeRunCube(CubeManager cm, Cube cube)
        {
            this.cm = cm;
            this.cube = cube;
        }

        void UpdateMoveType()
        {
            if (isForward && !isBack)
            {
                moveType = MoveType.Forward;
            }
            else if (!isForward && isBack)
            {
                moveType = MoveType.Back;
            }
            else
            {
                moveType = MoveType.Idle;
            }
        }

        public void StartMoveForward()
        {
            isForward = true;
            UpdateMoveType();
        }

        public void EndMoveForward()
        {
            isForward = false;
            UpdateMoveType();
        }

        public void StartMoveBack()
        {
            isBack = true;
            UpdateMoveType();
        }

        public void EndMoveBack()
        {
            isBack = false;
            UpdateMoveType();
        }

        public void SetHandleRotation(float value) => handleRotation = value;

        /// <param name="handleRotation">曲がる用のハンドルの回転 -1.0 ~ 1.0</param>
        public void OnUpdate()
        {
            if (moveType == MoveType.Idle)
            {
                return;
            }

            if (cm.IsControllable(cube))
            {
                Move();
            }
        }

        void Move()
        {
            var motorSpeed = CalcSpeed();
            cube.Move((int)motorSpeed.x, (int)motorSpeed.y, 100);
        }

        Vector2 CalcSpeed()
        {
            float moveSpeed = GetMoveSpeed();
            float rotationValue = Mathf.Clamp01(handleRotation * 1.5f); // 60度回転させたら最大の曲がりにする。
            float rotateSpeed = handleRotation * GetRotateBaseSpeed();

            return new Vector2(
                CalcSpeed(moveSpeed, rotateSpeed, rotateWeight),
                CalcSpeed(moveSpeed, -rotateSpeed, rotateWeight)
            );
        }

        float GetMoveSpeed()
        {
            switch (moveType)
            {
                case MoveType.Forward:
                    return forwardSpeed;
                case MoveType.Back:
                    return -backSpeed;
            }
            return 0;
        }

        float GetRotateBaseSpeed()
        {
            switch (moveType)
            {
                case MoveType.Forward:
                    return forwardRotateBaseSpeed;
                case MoveType.Back:
                    return -backRotateBaseSpeed;
            }
            return 0;
        }

        float CalcSpeed(float move, float rotation, float rotateWeight)
        {
            return move + rotation * rotateWeight;
        }
    }
}


モーターの回転速度を計算してCubeに渡している。

■ ポイント1 どのくらい傾けたら最大の横移動をするのかの調節

スマホを90度曲げないと最大の曲がり方をしないというのは大変なので調節した。

before after
90度傾けじゃないと最大カーブ速度でない 60~120度傾けで最大カーブ速度!
スクリーンショット 2022-12-18 15.39.52.png スクリーンショット 2022-12-18 15.39.46.png

図. スマホを傾けたときの「中心 -> ホームボタン」の向きにおける rotationValue。

.cs
float rotationValue = Mathf.Clamp01(handleRotation * 1.5f); // 60度回転させたら最大の曲がりにする。

上記コードによってピンクの範囲が最大の曲がり速度となる。

■ ポイント2 IsControllable

ドキュメント

.cs
        /// <summary>
        /// 前回のCubeへの送信から45ミリ秒以上空いていた時にTrueが返ります.
        /// </summary>
        public bool IsControllable(Cube cube)

毎フレームtoioに送信したくないので一定間隔待つ。

おわりに

toio動かすだけでとても楽しいのでぜひ試してみてください。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1