うどんてっくメモ

技術的なメモをまったりと

【Unity】DeviceSimulatorPluginを使ってDevice Simulatorの機能を拡張する

本記事で紹介および検証を行なっているツールのバージョンは次の通りです。

  • Unity 2021.3.1f1

バージョンによっては挙動に差異がある場合もありますので、ご了承ください。(少なくともUnity2021.1.0f1以降のバージョンで提供される機能となります)


UnityにはDevice Simulatorというエディタ上でモバイル端末などの出力をシミュレートするGame Viewが提供されています。

myudon.hatenablog.com

そんなDevice Simulatorですが、DeviceSimulatorPluginを活用し、機能を拡張することが可能です。

docs.unity3d.com

実際に上記の公式ページにあるサンプルを実装したスクリプトをAssets配下に置き、Device Simulatorの画面を見てみます。

Control Panelに「Touch Info」という名前のメニューが追加されました。実行し、タッチ操作を行うとその合計回数が表示されるようになっています。
サンプルではDeviceSimulatorPluginを継承したクラスを用意し、「Touch Info」というメニューの名前を指定するtitle、生成時処理を行うOnCreate、中身のUIを構成するOnCreateUIをoverrideして実装されています。 サンプルのコードを引用して次に示します。

public class TouchInfoPlugin : DeviceSimulatorPlugin
{
    public override string title => "Touch Info";

...

    public override void OnCreate()
    {
        deviceSimulator.touchScreenInput += touchEvent =>
        {
            m_TouchCount += 1;
            UpdateTouchCounterText();
            m_LastTouchEvent.text = $"Last touch event: {touchEvent.phase.ToString()}";
        };
    }

    public override VisualElement OnCreateUI()
    {
        VisualElement root = new VisualElement();

        m_LastTouchEvent = new Label("Last touch event: None");

        m_TouchCountLabel = new Label();
        UpdateTouchCounterText();

        m_ResetCountButton = new Button {text = "Reset Count" };
        m_ResetCountButton.clicked += () =>
        {
            m_TouchCount = 0;
            UpdateTouchCounterText();
        };

        root.Add(m_LastTouchEvent);
        root.Add(m_TouchCountLabel);
        root.Add(m_ResetCountButton);

        return root;
    }

...

}

DeviceSimulatorPluginではこれらのoverrideするメソッドの他に、DeviceSimulatorの参照がpublicで用意されています。

/// <summary>
///   <para>Extend this class to create a Device Simulator plug-in.</para>
/// </summary>
/// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulatorPlugin">`DeviceSimulatorPlugin` on docs.unity3d.com</a></footer>
public abstract class DeviceSimulatorPlugin
{
  internal string resolvedTitle;

  /// <summary>
  ///   <para>Device Simulator in which this plug-in is instantiated.</para>
  /// </summary>
  /// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulation.DeviceSimulatorPlugin-deviceSimulator">`DeviceSimulatorPlugin.deviceSimulator` on docs.unity3d.com</a></footer>
  public DeviceSimulator deviceSimulator { get; internal set; }

  /// <summary>
  ///   <para>Title for the plug-in UI.</para>
  /// </summary>
  /// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulation.DeviceSimulatorPlugin-title">`DeviceSimulatorPlugin.title` on docs.unity3d.com</a></footer>
  public abstract string title { get; }

  /// <summary>
  ///   <para>Called when Unity creates the Device Simulator window.</para>
  /// </summary>
  /// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulation.DeviceSimulatorPlugin.OnCreate">`DeviceSimulatorPlugin.OnCreate` on docs.unity3d.com</a></footer>
  public virtual void OnCreate()
  {
  }

  /// <summary>
  ///   <para>Called when Device Simulator window is destroyed.</para>
  /// </summary>
  /// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulation.DeviceSimulatorPlugin.OnDestroy">`DeviceSimulatorPlugin.OnDestroy` on docs.unity3d.com</a></footer>
  public virtual void OnDestroy()
  {
  }

  /// <summary>
  ///   <para>The VisualElement that this method returns is embedded in the Device Simulator window. If the method returns null, plug-in UI is not embedded.</para>
  /// </summary>
  /// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulation.DeviceSimulatorPlugin.OnCreateUI">`DeviceSimulatorPlugin.OnCreateUI` on docs.unity3d.com</a></footer>
  public virtual VisualElement OnCreateUI() => (VisualElement) null;
}

DeviceSimulator自体はとくに多くの操作や情報が取れるというわけではなく、タッチした際のイベントとなるtouchScreenInputを登録する程度です。

/// <summary>
///   <para>Class for interacting with a Device Simulator window from a script.</para>
/// </summary>
/// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulator">`DeviceSimulator` on docs.unity3d.com</a></footer>
public class DeviceSimulator
{
  internal ApplicationSimulation applicationSimulation;

  internal DeviceSimulator()
  {
  }

  public event Action<TouchEvent> touchScreenInput;

  internal void OnTouchScreenInput(TouchEvent touchEvent)
  {
    Delegate[] invocationList = this.touchScreenInput?.GetInvocationList();
    if (invocationList == null)
      return;
    foreach (Action<TouchEvent> action in invocationList)
    {
      try
      {
        action(touchEvent);
      }
      catch (Exception ex)
      {
        Debug.LogException(ex);
      }
    }
  }
}

TouchEventには座標とその動作となる情報が含まれています。

/// <summary>
///   <para>Representation of a single touch event coming from a Device Simulator. Subscribe to DeviceSimulator.touchScreenInput to receive these events.</para>
/// </summary>
/// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=TouchEvent">`TouchEvent` on docs.unity3d.com</a></footer>
public struct TouchEvent
{
  internal TouchEvent(int touchId, Vector2 position, TouchPhase phase)
  {
    this.touchId = touchId;
    this.position = position;
    this.phase = phase;
  }

  /// <summary>
  ///   <para>The unique identifier for the touch. Unity reuses identifiers after the touch ends.</para>
  /// </summary>
  /// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulation.TouchEvent-touchId">`TouchEvent.touchId` on docs.unity3d.com</a></footer>
  public int touchId { get; }

  /// <summary>
  ///   <para>On-screen position of the touch event. The zero point is at the bottom-left corner of the screen in pixel coordinates.</para>
  /// </summary>
  /// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulation.TouchEvent-position">`TouchEvent.position` on docs.unity3d.com</a></footer>
  public Vector2 position { get; }

  /// <summary>
  ///   <para>Phase of the touch event.</para>
  /// </summary>
  /// <footer><a href="https://docs.unity3d.com/2021.3/Documentation/ScriptReference/30_search.html?q=DeviceSimulation.TouchEvent-phase">`TouchEvent.phase` on docs.unity3d.com</a></footer>
  public TouchPhase phase { get; }
}

たとえば最後にタッチした座標のCanvas座標とワールド座標を常時表示するようにしたり、タッチした部分にヒットしたオブジェクトを表示したりといった機能ならば、このDeviceSimulatorPluginsを使ってお手軽に追加することが可能そうです。

参考・引用

docs.unity3d.com