18
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

KLabAdvent Calendar 2017

Day 24

UnityのHierarchyとInspectorを実機で使えるようにした話

Last updated at Posted at 2017-12-23

この記事は KLab Advent Calendar 2017 24日目の記事です。

はじめに

UnityEditorはゲームを動作させながら、HierarchyやInspectorを使ってゲームの内容を変更できるので、すごく開発が捗りますよね。

筆者自身、長い間Cocos2d-xのプロジェクトに関わっていたため、その辺りは特に感じているところです。

ただUnityEditorは便利なツールではあるのですが、実機開発になった途端に多くの機能が使えなくなってしまいます。
もちろん殆どの作業がUnityEditorだけで完結するため問題になる事はあまりありませんが、やはり実機確認が必要なシーンはどうしても発生しますよね。

例えば以下のようなもの。

  • UI/UXの調整
  • 機種依存問題(シェーダー等)
  • グラフィックの品質確認
  • プラットフォーム独自機能の実装
  • パフォーマンスチューニング

今回この問題を解決した話と、そこから派生したツールの紹介をしたいと思います。

課題と解決方法

まずHierarchy・Inspectorを実機でも使えるようにすれば、以下の作業が改善されると考えました。

  • UI/UXの調整
  • 機種依存問題(シェーダー等)

ベースとなる仕組みの実装

実機でHierarchy・Inspectorを使えるようにするためには以下の仕組みがあれば実現できそうです。
早速実装していきましょう。

  • UnityEditorと実機間で常時接続
  • ホットリロード

UnityEditorと実機間で常時接続

常時接続はUnityEditorと実機をTCP接続して実現します。
実機をTCPサーバー、UnityEditorをTCPクライアントとしてC#のSystem.Net.Sockets.TcpClientクラスを使って実装しました。

さらにadb、iproxyコマンドを使って接続ポートをUSBトンネリングする事でUSB経由の接続が可能となります。

# ios
iproxy [local port] [server port] [ios UDID]
# android
adb -s [Android serial_number] forward tcp:[local port] tcp:[server port]

これでUnityEditorと実機間で高速かつオフラインでの双方向通信が可能となりました。

ホットリロード

ホットリロードについては、インタプリタ型言語のLuaを使って実現する事にします。

選定理由はUnityに対応しているライブラリがいくつかあったのと、社内的にユーザーが多いからです。
ただ、いくつかのライブラリを検証しましたが、Unityのバージョン1のせいなのかまともに動作しないものや、IL2CPPに対応していないものがあったりして、結果的にsluaというライブラリを採用しました。

ちなみに検証段階では存在しなかったので試していないですが、最近tencentから公開されたxLuaも良さそうです。
(※今回のライブラリの選定ですが、あくまでツール利用なのでパフォーマンス、安定性等は考慮に入れていません。)

sluaからUnityAPIの実行例

以下のサンプルコードのようにUnityAPIは同名になっているのでLuaの基本構文さえ覚えれば、すんなり実装できると思います。

Luaのサンプルコード

import UnityEngine
function main()
    local obj = GameObject.Find(Player)
    obj:SetActive(false)
end

Luaを利用したUnityEditorと実機間のデータの送受信

Luaスクリプトを使ったUnityEditorと実機のやり取りが簡単にできる仕組みを作っていきます。

以下がUnityEditor側から実行するC#のコードになります。

// Luaスクリプト
var luaScript = @"
import ‘UnityEngine’
local json = require 'json'
function main()
  local player = {}
  player.Name = 'Taro'
  return json.decode(player)
end
";

// UnityEditor(C#)からLuaファイルを送信
Client.SendLuaScript(
  luaScript,
  json =>
  {
    var player = JsonUtility.FromJson<Player>(json);
  }
);

Client.SendLuaScript関数の処理の流れ

  1. UnityEditorからClient.SendLuaScript関数でLuaスクリプトを実機に送信。
  2. 実機でLuaスクリプトのmain関数を実行し、main関数の戻り値をUnityEditorに送信。
  3. Client.SendLuaScriptの第二引数のコールバック関数が呼ばれます。引数の変数jsonはmain関数の戻り値です。

Hierarchy、Inspectorの実装

ベース機能ができたので、HierarchyとInspectorを実装していきます。

Hierarchy

まずはGameObjectの階層構造をJson化するLuaスクリプトを作成します。

import UnityEngine
local json = require 'json'

local index = 1
function main()
  local hierarchy = {}
  for i=0, UnityEngine.SceneManagement.SceneManager.sceneCount-1 do
    local sceneObj = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i)
    local scene = {}
    scene.Name = sceneObj.name
    hierarchy[index] = scene
    -- Scene内のGameObjectを取得
    setGameObjects(scene, hierarchy)
  end
  return json.decode(hierarchy)
end

このLuaスクリプトを実機に送信して、受け取ったJsonを使ってUnityEditor拡張で作成したオリジナルのHierarchyウィンドウを表示すると完成です。

hierarchy.png

Inspector

InspectorもHierarchyと同様にGameObjectの情報(コンポーネント等)をJson化するLuaスクリプトを使ってInspectorウィンドウを作成します。

inspector_recttransform.png

InspectorからTransformを変更したいので、Transformを変更するLuaスクリプトを実装します。

var luaScript = @"
import 'UnityEngine'
function main(obj)
  obj.transform.position = Vector3(@@position.x@@, @@position.y@@, @@position.z@@)
end
";

// Luaスクリプトの一部をInspectorに入力した値に置換
luaScript = luaScript
  .Replace("@@position.x@@", inspector.RectTransform.Position.x)
  .Replace("@@position.y@@", inspector.RectTransform.Position.y)
  .Replace("@@position.z@@", inspector.RectTransform.Position.z);

// UnityEditor(C#)からLuaファイルを送信
Client.SendLuaScript(
  luaScript,
  json => {}
);

このようにInspectorで入力した値をLuaスクリプトに反映させてから実機に送信する事で、InspectorからのTransformの変更が可能となります。

Inspecterからシェーダーを置き換える

次にInspectorからのシェーダーの置き換えに対応させていきます。
Transformの実装と異なる点はシェーダーファイルを実機に転送する必要があるところです。

処理手順

  1. Inspectorで選択したシェーダーをアセットバンドル化。
  2. アセットバンドルファイルを実機に転送。
  3. シェーダーを置き換えるLuaスクリプトを送信。

シェーダーを置き換えるLuaスクリプト

-- 引数のobjはInspectorに表示されているGameObjectのインスタンスです。
function main(obj)
  local ab = AssetBundle.LoadFromFile("/path/to/assetbuneles/sample.shader.assetbundle")
  local assets = ab:LoadAllAssets()
  local shader = assets[1]
  ab:Unload(false)
  local component = obj:GetComponent('Renderer')
  component.materials[1].shader = shader
end

課題の解決

  • UI/UXの調整
  • 機種依存問題(シェーダー等)

これで上記の課題を改善する事ができました。
UI/UXについてはプランナーなどの非技術だけで調整ができるようになったところも良かったです。
時間のかかるシェーダーの実機開発も改善されました。

その他のツールの紹介

今回実装した仕組を使って作成したツールが他にもあるので、いくつか紹介したいと思います。

ツール紹介1: DeviceExplorer

実機のディレクトリの内容を確認できるEditor拡張です。
UnityEditorのプロジェクトウィンドウのように実機のディレクトリがUnityEditor内にツリー状で表示されます。

explorer.png

ファイルを選択するとファイルのフォーマットに合わせた内容が先ほど作成したInspectorに表示されるようになっています。
例えばアセットバンドルファイルの場合は内包されているアセットの内容を表示したり、
テクスチャの場合はInspector上でプレビュー表示をする事もできます。

inspector_preview.png

ちなみにLuaからC#の標準クラスを呼び出せるラッパークラスを作成して使いました。
例えばLuaはIO処理が弱いのでC#のSystem.IOをLuaから呼び出せるようにして使ったりしています。

sluaを使うと簡単にC#のラッパークラスが作成できますが、今回はsluaの使い方については省略させて頂きます。

Luaサンプルコード

import 'UnityEngine'
import 'CSharpWrapper.IO'

function main()
  parse_directory("/data/data/sample.org/files/assets")
end

function parse_directory(dir)
  local files = Directory.GetFiles(dir)
  for i = 1, #files do
    Debug.Log(files[i])
  end
  local dirs = Directory.GetDirectories(dir)
  for i = 1, #dirs do
    Debug.Log(dirs[i])
    parse_directory(dirs[i])
  end
end

こうなるとC#のコードを書いているのと殆ど変わらなくなってきますよね。

ツール紹介2: AssetTransfer

実機にUSB経由で高速にアセットを転送できるツールです。
デバッガーなどのUnityEditorを所有していないかたでも使用できるようにスタンドアローンアプリとして提供しています。
PCに接続している複数の端末に同時に転送する事もできるようにしています。
デバッガーが毎日多くの端末を準備しているのですが、その時間の短縮にも役立っています。
さらにUSBを使うのでWifi回線の渋滞も改善されます。

ツール紹介3: ApplicationMonitor

クライアントアプリの情報をリアルタイムに監視する事ができるUnityEditor拡張です。
情報とは、パフォーマンス情報やアプリ画面に表示されていないような内部データ等になります。
このようなデバッグ情報は今まではアプリ画面に出していましたが、UnityEditor上に表示するようになったので、画面サイズの制限がなくなったのと描画負荷もかからなくなりました。

補足

この仕組みですがUnityEditor側はWindows・Macどちらでも動作し、
実機側もiOS・AndroidのOSのバージョンに制限なく動作します。

実機だけではなくUnityEditorで動作しているアプリでも使用可能です。

おわりに

いくつかツールを紹介しましたが、
常時接続とLuaを使えば実機開発の多くの事が改善するのでオススメです。

ちなみにUnity2017からは以下の2つのクラスが追加されていたので、常時接続環境はこちらで代替できそう気がします。
(使った事はないので使えなかったらごめんなさい。)

とうとう最終日となりました。
明日、25日目はtakahashi-yoさんです。よろしくお願いします。

  1. Unityのバージョンは5.6を使用しています。

18
8
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
18
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?