😭

ZIG SIM × Processing|スマホをゲームコントローラにしよう!

2021/12/02に公開

まとめると

・ZIG SIMというアプリをスマホに入れるだけでスマホから加速度センサ・ジャイロセンサ・GPSなどのデータを取得できる
・UDP/TCP通信でProcessingにデータを送れる
・Processingへの送信の仕方(コードの書き方)と取得できるセンサ一覧をまとめた
・ZIG SIMでコントローラを自作して、Processingで作ったゲームと組み合わせてみた

はじめに

こちらはProcessing Advent Calender 2021の2日目の記事です(参加4年目).

今回はゲーム制作でお世話になっているZIG SIMを紹介します.電子工作がめんどくさいときやお財布の事情で電子部品を買いたくないとき使っています.

ちなみにZIG SIMとProcessingでこんなものを作りました.

風呂桶を投げるゲーム
風呂桶の中にスマホを入れています.
https://youtu.be/UElFVddK2CQ

手裏剣を投げるゲーム

https://youtu.be/n7xUBcvFGt8

ZIG SIM

ZIG SIMは株式会社1→10(ワントゥーテン)さんが開発しているプロトタイピング用のスマホアプリです.

「Google Playストア」や「Apple Store」で無料ダウンロードできます.

スマホに搭載しているセンサ(加速度センサ・ジャイロセンサ・GPSなど)のデータを取得して,UDP通信またはTCP通信でPCや他のスマホにデータを送ることができます.

そのため,ProcessingやUnityなどで制作したゲームのコントローラに使えます.

公式資料

ZIG-Project 公式サイト

アプリの解説をしています.
ここからでもアプリのダウンロードができます.

ZIG SIM Document

具体的にどんなデータを取得できるのか,それがどういった形式で送信されるのかを記載しています.
この記事ではさわりしか紹介しないので詳しくはこちらのドキュメントを読んでください.
(もしそれでも分からなかったらTwitterで僕にDMください)

実用例

https://youtu.be/i6pv5nflEmc

取得可能なデータ一覧


AndroidとiPhoneによって搭載しているセンサが違います.
共通部分とiPhone限定, Android限定の部分を分けて紹介します.

共通部分

センサ名 取得名
加速度 accel
重力加速度 gravity
ジャイロ gyro
スマホの向き quaternion
方位磁石 compass
気圧 pressure
緯度・経度 gps
タッチ座標 2d touch
タッチ圧力 3d touch
近接センサ proximity monitor

iPhone

センサ名 アプリ登録名
タッチ半径 touch radius
位置測定 beacon
入力音量 miclevel
イヤフォン remotecontrol

Android

センサ名 アプリ登録名
明るさ light

Processingとの通信方法

今回はジャイロセンサとタッチセンシングのやり方を紹介します.

大前提としてスマホとPCを同じWi-Fiに接続してください!!!

なんか通信できないと思ったら各々が繋いだWi-Fiが別だったとかあるあるなので,強調しておきます.

ZIG SIMの設定方法

1. 取得したいセンサにチェックを入れる
今回は「GYRO」と「2D TOUCH」にチェックを入れてください.

2. IP ADDRESS(IPアドレス)を入力する
PCのIPアドレスを入力してください.
調べ方はこちらにまとまっています.
WindowsのIPアドレスの調べ方
MacのIPアドレスの調べ方

3. PORT NUMBER(ポート番号)を入力する
今回は8888と入力してください.

4. STARTを押す
下段の真ん中にある 「START」を押したら送信開始します.

これでZIG SIM側の準備は大丈夫です!
あとはProcessingでコードを書いて,実行するだけです.

JSONデータの中身

ZIG SIMが送信したJSONデータをProcessingで受信します.
JSONについて分からない方はこちらの記事を参考にしてください.
[Processing] JSONの読み込み書き込み

iPhoneの場合

ZIG SIMが送信するセンサの種類とデータ構造についてからコピペしました.
取得できるセンサを全部チェックいれたときのJSONデータです.

{
    "device":{ // 絶対に飛ぶ基本情報
        "name" : "iPhone 6s Plus",
        "uuid":"0123456789abcdef",
        "os":"ios",
        "osversion":"10.0.2",
        "displaywidth":1920,
        "displayheight":1080
    },
    "timestamp" : "2016/04/06 15:36", // 絶対に飛ぶ基本情報
    "sensordata":{
        "accel" : { // "ACCEL"選択時に出力される
            "x" : 1.00,
            "y" : 1.00,
            "z" : 1.00
            },
        "gravity" : { // "GRAVITY"選択時に出力される
            "x" : 1.00,
            "y" : 1.00,
            "z" : 1.00
            },
        "gyro" : { // "GYRO"選択時に出力される
            "x" : 1.00,
            "y" : 1.00,
            "z" : 1.00
            },

        "quaternion": { // "QUATERNION"選択時に出力される
            "x" : 1.00,
            "y" : 1.00,
            "z" : 1.00,
            "w" : 1.00
        }, 
        "compass": { // "COMPASS"選択時に出力される
            "faceup": false,
            "compass": 180.0
        }, 
        "pressure": { // "PRESSURE"選択時に出力される
            "pressure" : 1.000,
            "altitude" : 1.000
        }, 
        "gps": { // "GPS"選択時に出力される
            "latitude" : 180.0,
            "longitude" : 180.0
        }, 
        "miclevel":{
            "average":-33.25574493408203,
            "max":-26.98263359069824
        },
        "proximitymonitor":{
            "proximitymonitor":false
        },
        "remotecontrol":{
            "volumeup":false,
            "playpause":false,
            "volumedown":false
            },

        "touch" : [  // "2D Touch"、"TOUCH RADIUS"、"3D TOUCH"のどれかが選択されていると出力される 
                       {    
                            "x" : 0.5, // "2D Touch"選択時に出力される
                            "y" : 0.3, // "2D Touch"選択時に出力される
                            "radius" : 1.0, // "Touch RADIUS"選択時に出力される
                            "force" : 1.0  // "3D Touch"選択時に出力される
                      },
                      {  // 複数のタッチポイントがあれば出力される。iOSの場合最大5点
                            "x" : 0.1, 
                            "y" : 0.1,
                            "radius" : 2.0,
                            "force" : 0.5
                      },
        ]
        "beacon" : [ // "BEACON"選択時に出力される
                       {    
                            "uuid" : XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,
                            "rssi" : -100,
                            "major" : 100,
                            "minor" : 1,
                      },
                     {    // 複数のBeaconが検出されてれば出力される。
                            "uuid" : XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,
                            "rssi" : -50,
                            "major" : 101,
                            "minor" : 1,
                      }
        ]
    }
}

Androidの場合はこちら

自分のスマホから取得しました.

{
  "device": {
    "displaywidth": 1440,
    "os": "Android",
    "name": "Pixel 3 XL",
    "osversion": "12",
    "uuid": "zawa",
    "displayheight": 2621
  },
  "sensordata": {
    "proximitymonitor": {"proximitymonitor": false},
    "quaternion": {
      "w": 0.9159276485443115,
      "x": -0.004516989924013615,
      "y": -0.0068184928968548775,
      "z": 0.401260107755661
    },
    "compass": {
      "compass": -55.99816256785344,
      "faceup": 0
    },
    "light": {"light": 172.96188354492188},
    "gravity": {
      "x": -0.008886781521141529,
      "y": 0.013750473037362099,
      "z": 1.0008865594863892
    },
    "touch": [//2本指で触ったのでデータが2つ取得しています
      {
        "x": -0.011111111111111072, //2D TOUCHをONにすると取得できます
        "y": -0.3216329645173598, //2D TOUCHをONにすると取得できます
        "force": 0.2698412835597992 //3D TOUCHをONにすると取得できます
      },
      {
        "x": 0.4194444444444445,
        "y": -0.1888592140404426,
        "force": 0.1428571492433548
      }
    ],
    "pressure": {
      "altitude": 0,
      "pressure": 1027.0250244140625
    },
    "gps": {//個人情報なので値は適当なものに変えています
      "latitude": 11.4514051,
      "longitude": 33.4627737
    },
    "accel": {
      "x": 0.0019250899786129594,
      "y": -0.006080803461372852,
      "z": -0.005204582121223211
    },
    "gyro": {
      "x": -0.001997369108721614,
      "y": 6.657897029072046E-4,
      "z": -0.003595264395698905
    }
  },
  "timestamp": "2021/11/29 03:10:57.564"
}

Processingのコード

UDPライブラリのインストール

以下の手順でUDPライブラリをインストールしてください.

  1. Processingの上のバーにある「スケッチ」から「ライブラリをインポート」,「ライブラリを追加」の順に選びます

  2. 「Libraries」で「UDP」をインストールしてください(検索バーを使うとすぐ見つかります)

GYROを取得する

こちらのコードをコピペしてください.

import hypermedia.net.*;

UDP udp;

void setup() {
  udp = new UDP( this, 8888 );//ZIG SIMの設定と同じにする
  udp.listen( true );
}

void draw() {
}

void receive( byte[] data, String ip, int port ) {

  if (data == null)return;

  String message = new String(data);

  JSONObject json = parseJSONObject(message);
  JSONObject gyroData = json.getJSONObject("sensordata").getJSONObject("gyro");
  float x = gyroData.getFloat("x");
  float y = gyroData.getFloat("y");
  float z = gyroData.getFloat("z");

  println("x: " + x);
  println("y: " + y);
  println("z: " + z);
  println();
}

出力結果

https://youtu.be/HG1lqKhHwLw

基本的にこの書き方で他のデータも取得できます.
適宜データに合わせて

  JSONObject gyroData = json.getJSONObject("sensordata").getJSONObject("gyro");

gyroの部分を取得したいデータの名前に変えて

  float x = gyroData.getFloat("x");

の部分をxをの部分を変えたり,型によってはgetFloat()getInt()getBoolean()に変えてください.

TOUCHを取得する

TOUCHBEACONだけはこんな感じで値を取得します.

import hypermedia.net.*;

UDP udp;

void setup() {
  udp = new UDP( this, 8888 );//ZIG SIMの設定と同じにする
  udp.listen( true );
}

void draw() {
}

void receive( byte[] data, String ip, int port ) {

  if (data == null)return;

  String message = new String(data);

  JSONObject json = parseJSONObject(message);
  JSONArray touchData = json.getJSONObject("sensordata").getJSONArray("touch");

  for (int i = 0; i < touchData.size(); i++) {
    JSONObject touch = touchData.getJSONObject(i);
    float x = touch.getFloat("x");
    float y = touch.getFloat("y");

    println(i);
    println("x: " + x);
    println("y: " + y);
    println();
  }
}

出力結果

https://youtu.be/oIvbgovzXqw

ちなみにtouchにおいてxの範囲は(左端) -1 ≦ x ≦ 1 (右端)で,yの範囲は(上端) -1 ≦ y ≦ 1になります.

そのためProcessingの実行画面とサイズを合わせるにはこういう工夫が必要です.

import hypermedia.net.*;

UDP udp;
float ballX, ballY;

void setup() {
  size(600, 400);
  udp = new UDP( this, 8888 );//ZIG SIMの設定と同じにする
  udp.listen( true );

  ballX = 0;
  ballY = 0;
}

void draw() {
  background(0);
  fill(255);
  ellipse(ballX, ballY, 50, 50);
}

void receive( byte[] data, String ip, int port ) {

  if (data == null)return;

  String message = new String(data);

  JSONObject json = parseJSONObject(message);
  JSONArray touchData = json.getJSONObject("sensordata").getJSONArray("touch");

  if (touchData.size() > 0) {//一本目の指しか使わないためfor文からif文に変更
    JSONObject touch = touchData.getJSONObject(0);
    float x = touch.getFloat("x");
    float y = touch.getFloat("y");
    ballX = (x + 1) * width/2;//工夫部分
    ballY = (y + 1) * height/2;//工夫部分
  }
}

出力結果

https://youtu.be/SofXhNBL6hk

おわりに

この記事だけでZIG SIMとProcessingが連携できるように書いたらめちゃくちゃ長くなってしまいました……
でも,これができれば電子工作しないで自作コントローラを作ることができます!
ぜひZIG SIMとProcessingを組み合わせてコンテンツを作って見てください!

著者プロフィール

こんなの作ってる大学院生です.

Discussion