LoginSignup
16
14

More than 5 years have passed since last update.

Unityで加速度センサを用いてダーツを投げるまで

Posted at

はじめに

この記事は,Unity上でスマートフォンの加速度センサを用いてダーツを投げるまでの方法を記していきます。

環境は Unity 2017.3 です。
スマートフォンはAndroid端末を用います。
ビルド(実機テスト)に関しての詳細は省くので,分からなかった場合は他のサイト等を参考にしてください。
(Androidであればここ:link:,iOSであればここ:link:など)

全体像

はじめに,ダーツを投げるまでの全体像を確認します。

  1. ダーツを用意する
  2. 加速度を取得する
  3. 加速度を計算する
  4. ダーツを生成し,速度を与える
  5. 実機テスト

こんな感じです。

作る

ダーツを用意する

まず投げるためのダーツがなければ始まりません。
本格的なものを用意してもいいし,デフォルトのsphereを投げるとかでも良いのですが,今回はデフォルトの3Dオブジェクトを組み合わせてそれっぽい見た目のものを作っていくことにします。

今回はCube2つを薄くして組み合わせてフライトに,Capsule1つを伸ばして本体にしてみました。

階層と設定はこんな感じです。

階層
Dart ┬ Flight ┬ Flight1 (羽根)
     |        └ Flight2 (羽根)
     └ Schaft (本体)  
部位 種類 Position(X, Y, Z) Rotation(X, Y, Z) Scale(X, Y, Z)
Flight Empty (0, 0, 0) (0, 0, 0) (0, 0, 0)
Flight1 Cube (0, 0, -1.54) (0, 0, 90) (1, 0.02, 1)
Flight2 Cube (0, 0, -1.54) (0, 0, 0) (1, 0.02, 1)
Schaft Capsule (0, 0, 0) (90, 0, 0) (0.1, 1.8, 0.1)

Materialも適当に作って見た目を変えるとこんな感じになります。

https://gyazo.com/f126e80731a3aa92637e61f6e80866e3

最後に,物理挙動をさせるためにRigidbodyをInspector上でアタッチし,
後から使い回せるようにPrefab化しておきます。

https://gyazo.com/259bbcd603e4e70bd6a8b76dd66f374e

また,Prefab化したらHierarchyビュー上のダーツは削除しておきましょう。

加速度を取得する

次にダーツを投げるために加速度を取得していきます。

加速度を取得
var acceleration = Input.acceleration;

Unityだとこれだけで簡単に加速度を取得できてしまいます。

今回は加速度から速度を計算したいため,直近の15フレームの加速度を保持しておくことにします。
後で使いやすいようにQueuex,y,zの3種類を別々に保持しておきます。
script全体はこんな感じになります。

AccelerationScript.cs
using System.Collections.Generic;
using UnityEngine;

public class AccelerationScript : MonoBehaviour
{
    private Queue<float> lastXAccels = new Queue<float>();
    private Queue<float> lastYAccels = new Queue<float>();
    private Queue<float> lastZAccels = new Queue<float>();

    // 保持するフレーム数
    private readonly int ACCEL_NUM = 15;

    private void Start()
    {
        // 加速度のキューを0埋めしておく
        for (int i = 0; i < ACCEL_NUM; i++)
        {
            lastXAccels.Enqueue(0);
            lastYAccels.Enqueue(0);
            lastZAccels.Enqueue(0);
        }
    }

    private void Update()
    {
        float x = Input.acceleration.x;
        float y = Input.acceleration.y;
        float z = Input.acceleration.z;

        // 一番古い加速度をDequeueで削除。最新の加速度をEnqueueで追加
        lastXAccels.Dequeue();
        lastXAccels.Enqueue(x);
        lastYAccels.Dequeue();
        lastYAccels.Enqueue(y);
        lastZAccels.Dequeue();
        lastZAccels.Enqueue(z);
    }
}

Queueを使うためにusing System.Collections.Generic;するのを忘れずに。

加速度を整形する

次にダーツを飛ばせるように加速度を速度に整形していきます。(すごい!)

はじめに,スマホの持ち方(向き)を決めておきます。
(向きによってx,y,zが入れ替わるので注意が必要です)

今回は,奥行方向に関してはx,上下左右方向に関してはy,zを使うことにします。

奥行方向は直近のフレームの中の最大の絶対値を利用することにします。
Linqを使って,こんな感じに取得できます。
Mathf.Clamp()で0~2の範囲に抑えて,定数を掛けます。
* 20の部分はマジックナンバーです。最後に調整していきます。

奥行方向
using System.Linq;

var xMax = lastXAccels.Select(x => Mathf.Abs(x)).Max();
zVelocity = Mathf.Clamp(xMax, 0, 2) * 20;

左右上下方向は"ブレ"により決定することにします。
"ブレ"は平均値二乗誤差で計算することにします。
これもまたLinqを使って,こんな感じに書けます。
Average()で平均値を取得し,平均値との差の絶対値の合計を要素数Countで割ります。
* 15* 20もマジックナンバーなので,最後にダーツの飛び具合を見ながら調整してください。

上下左右方向
var yAverage = lastYAccels.Average();
var yVariance = lastYAccels
    .Select(y => Mathf.Abs(y - yAverage))
    .Sum() / lastYAccels.Count;
yVelocity = yVariance * Mathf.Sign(lastYAccels.Last()) * 15;

var zAverage = lastZAccels.Average();
var zVariance = lastZAccels
    .Select(z => Mathf.Abs(z - zAverage))
    .Sum() / lastZAccels.Count;
xVelocity = zVariance * Mathf.Sign(lastZAccels.Last()) * 20;

全体をまとめるとこんな感じになります。
CalcVelocity()を外部から呼ぶと,直近の加速度から速度を計算して返してくれる感じです。

AccelerationScript.cs
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class AccelerationScript : MonoBehaviour
{
    private Queue<float> lastXAccels = new Queue<float>();
    private Queue<float> lastYAccels = new Queue<float>();
    private Queue<float> lastZAccels = new Queue<float>();

    // 保持するフレーム数
    private readonly int ACCEL_NUM = 15;

    private void Start()
    {
        // 加速度のキューを0埋めしておく
        for (int i = 0; i < ACCEL_NUM; i++)
        {
            lastXAccels.Enqueue(0);
            lastYAccels.Enqueue(0);
            lastZAccels.Enqueue(0);
        }
    }

    private void Update()
    {
        float x = Input.acceleration.x;
        float y = Input.acceleration.y;
        float z = Input.acceleration.z;

        // 一番古い加速度をDequeueで削除。最新の加速度をEnqueueで追加
        lastXAccels.Dequeue();
        lastXAccels.Enqueue(x);
        lastYAccels.Dequeue();
        lastYAccels.Enqueue(y);
        lastZAccels.Dequeue();
        lastZAccels.Enqueue(z);
    }

    public Vector3 CalcVelocity()
    {
        float xVelocity;
        float yVelocity;
        float zVelocity;

        // |x|の最大値
        var xMax = lastXAccels.Select(x => Mathf.Abs(x)).Max();
        // z方向の速度(奥行方向の速度をxMaxを元に計算)
        zVelocity = Mathf.Clamp(xMax, 0, 2) * 20;

        // yの絶対値平均誤差
        var yAverage = lastYAccels.Average();
        var yVariance = lastYAccels
            .Select(y => Mathf.Abs(y - yAverage))
            .Sum() / lastYAccels.Count;
        yVelocity = yVariance * Mathf.Sign(lastYAccels.Last()) * 15;

        // zの絶対値平均誤差
        var zAverage = lastZAccels.Average();
        var zVariance = lastZAccels
            .Select(z => Mathf.Abs(z - zAverage))
            .Sum() / lastZAccels.Count;
        xVelocity = zVariance * Mathf.Sign(lastZAccels.Last()) * 20;

        var vec = new Vector3(xVelocity, yVelocity, zVelocity);

        return vec;
    }
}

ダーツを飛ばす

最後に,上で作ったメソッドを用いてダーツを投げれるようにしていきます。
今回は画面タップでダーツが投げられるようにします。
するとスクリプトはこんな感じになります。

Throw.cs
using UnityEngine;

public class Throw : MonoBehaviour
{
    [SerializeField]
    private GameObject dartsPrefab;
    [SerializeField]
    private AccelerationScript accelerationScript;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            var velocity = accelerationScript.CalcVelocity();
            var dart = Instantiate(dartsPrefab);
            dart.GetComponent<Rigidbody>().velocity = velocity;
        }
    }
}

あとはInspector上で対応させていきます。

  1. Hierarchy上でCreate Empty(GameManagerとかの名前に変えておく)
  2. そこにInspector上でAccelerationScriptとThrowをアタッチ
  3. さらにInspector上でThrowのDartsPrefabに先程作ったダーツのPrefabをセット
  4. さらにInspector上でThrowのAcceleration ScriptにGameManagerをD&Dしてスクリプトをセット

するとこんな感じになります。

image.png

実機テスト

実機テストの前に,カメラの位置とダーツの角度を調整しておきます。
Main CameraのPositionのZを-10から-5に,PrefabのDartのRotationを0から-30に変更します。

このままで実行すると,クリック毎にダーツが生成されてそのまま落ちていくのが確認できるはずです。
これは,パソコンだと加速度が必ず(0, 0, 0)として認識されるためです。

加速度を確認するには実機(スマホ)で動かしてみることが必須なので,実機でテストしていきます。
JDKなどを入れていない場合は はじめに 内のリンクを参考に設定してください

まず, Ctrl/Cmd + Shift + BBuild Settingsを開き,ビルド設定をします。
Add Open Scenesかなにかでビルドするシーンを設定します。
https://gyazo.com/484647b6d9ab9f841d2d744cf1c6a9a1

次に,Bulid Settings内のPlayer Settings...を押してCompany NameProduct Nameを適当に設定します。
また,Player Settings内でOther Settingsを開き,Package Nameも併せて変更します。

https://gyazo.com/faf0f22fe5c504d996b4761b944c6a3b

さらに画面を横画面に固定するため,Player Settings内でResolution and Presentationを開き,OrientationDefault OriantationLandscape Rightに設定しておきます。

https://gyazo.com/791c5c9f01aca70031ae6fdb286319c3

そうしたらBuild And Runを押し,保存先を指定してビルドと実行をします。

端末を振りながらタップするとダーツが飛んでいくことが確認できるはずです。

以上です。お疲れ様でした!

おまけ

現状だとダーツは回転せずにそのまま飛んでいくので,横方向にランダムに回転させながら,飛んでいく方向に先端を向けさせてみようと思います。

DartsScript
using UnityEngine;

public class DartsScript : MonoBehaviour
{
    private Rigidbody rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        float angularVelocity = Random.Range(-100, 100);
        // 進行方向を向かせる
        var look = Quaternion.LookRotation(rb.velocity);
        var z = transform.eulerAngles.z;
        var angle = Quaternion.Euler(0, 0, z + angularVelocity * Time.deltaTime);
        transform.rotation = look * angle;
    }
}

これをダーツのPrefabにアタッチしておけばOKです!

https://gyazo.com/7a9cace8cbde7df92105572f06a000d0

ここまで読んでくださりありがとうございました!

16
14
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
16
14