クマテクブログ

日々のメモ

Unityがプロジェクト選択画面で固まる謎の不具合の回避方法

昨日からプロジェクト選択画面で特定のプロジェクトだけ起動しなくなると言う謎の不具合が発生しました。

自分の知り合いも何人か同じ状況になっているらしくフォーラムでも同様の問題が発生してる人がたくさんいるようでした。
Error | Unity Community

自分は5.5、5.6で発生することを確認し、知り合いは2017で発生したそうです。
MacWindows共に発生することを確認しました。

Workaround

色々試してみてWorkaroundを見つけたので紹介したいと思います。

  • PCのネットワークを切断して起動
  • シーンを直接起動
  • /etc/hostsに「127.0.0.1 update.unity3d.com」を追加

このどれかの方法で無事起動させることができます。

原因

原因はいったいなんなのかUnity Editorのログをみてみます。

Downloading http://update.unity3d.com/5.6/ivy.xml to /var/folders/lc/7qgqzqvj4bs6shhq1rj88mm00000gp/T/unity/49bf52e2-32e5-4419-91db-05383a6d686d/ivy.xml
Registered platform support modules in: 0.180029s.
Native extension for OSXStandalone target not found
Native extension for WebGL target not found
Loading native library: UnityEditor.iOS.Native at /Applications/Unity/PlaybackEngines/AppleTVSupport/UnityEditor.iOS.Native.dylib
[usbmuxd] Start listen thread
[usbmuxd] Listen thread started
[usbmuxd] Send listen message
Native extension for tvOS target not found
Native extension for iOS target not found
Native extension for Android target not found
Begin MonoManager ReloadAssembly
Shutting down Remote Indexer
Task failed: Downloader Task
Cancelling tasks, domain is going down

ログはここで止まっています。
Unity Edtiorの動きをみるとずっとローディングを続けていて無限ループに陥ってるような挙動をします。

ログに書かれている「http://update.unity3d.com/5.6/ivy.xml」にアクセスすると404になっているのでUnityのサーバの問題でしょうか?

詳細な原因はソースを見れない為わからないのですが、おそらくivy.xmlが404で取得できない時、特定の条件が重なると(自分の環境ではScriptが大量にあるプロジェクトで起きやすかったです)発生するように思えます。

なのでivy.xmlを参照しているプロジェクト選択画面を回避して直接シーンファイルを開いたり、ネットワークを切断したりする方法が有効なようです。

実はこれをみると結構昔のバージョンから発生している問題のようで、Unityのサーバに問題が起きた際に浮き彫りになってきたのでしょうか?
Error | Unity Community

おそらくサーバが復旧したら普通に起動できるようになるのかとは思うのですが、根本的なUnity Edtiorのエラー処理を修正して欲しいところであります。


現在は復旧して公式でアナウンスがされたみたいです。
Unity Editor がハングアップする問題 – ユニティ・テクノロジーズ・ジャパン合同会社

ARKitでどこでもドア

これを見て一ヶ月前に真似て作ったサンプル

いろんなひとがどこでもドアを作ってたのでせっかくだから自分もあげてみる。

ソースコード自体は前回のものを流用し、そこにいくつかのShaderを追加で実装しただけのインスタント実装です。

ちなみにAppleがiOS11で一番推したい機能がARKitだそうです。
iOS11正式リリースに合わせて何か面白いARKitアプリを作ってリリースしてみるのも面白そうですね。

iOS11のARKitをUnityから使用してみる

iOS11が発表されiOSにARKitという新しいARサポートが入りました。
こちらはUnityやUnreal EngineからPluginを通して使用する事できるそうなのでUnityを使ってオブジェクトを表示してみました。

必要なもの

  • iOS11にUpdateしたiPhone 6s以上の端末(ARは実機じゃないと確認できません)
  • XCode 9(現在β)
  • Unity 5.6.1p1以上
  • Unity ARKit Plugin

STEP

#1. Unity ARKit Pluginをプロジェクトにインポート
#2. Player SettingsのCamera Usage Descriptionを設定(これを設定しないとアプリがクラッシュします。)
#3. Main CameraにUnityARVideoコンポーネントを追加し、UnityARVideoのClear MaterialにYUVMaterialを設定
#4. UnityARGeneratePlane Componentをシーンのオブジェクトに追加(これを追加すると平面を検知してくれるようになります。)

これで準備は整いました。
あとは下記のようなソースを追加してみます。
平面の検知後、画面をタップした所に平面があればオブジェクトを生成するというスクリプトになります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.iOS;

public class Spawner : MonoBehaviour
{
	public GameObject catPrefab;
	public float createHeight;
	private MaterialPropertyBlock _props;

	void Start()
	{
		_props = new MaterialPropertyBlock();
	}

	void CreateCat(Vector3 position)
	{
		Instantiate(catPrefab, position, Quaternion.identity);
	}

	void Update()
	{
		if (Input.touchCount > 0 )
		{
			var touch = Input.GetTouch(0);
			if (touch.phase == TouchPhase.Began)
			{
				var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);

				ARPoint point = new ARPoint {
					x = screenPosition.x,
					y = screenPosition.y
				};

				List<ARHitTestResult> hitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface().HitTest (point, 
				ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent);
                
				if (hitResults.Count > 0)
				{
					foreach (var hitResult in hitResults)
					{
						Vector3 position = UnityARMatrixOps.GetPosition (hitResult.worldTransform);
						CreateCat (new Vector3 (position.x, position.y + createHeight, position.z));
						break;
					}
				}
			}
		}
	}
}

無事カメラ空間の中にオブジェクトが生成されました。

https://raw.githubusercontent.com/wiki/cyario/ARKitSample/images/cats.gif

RigitBodyで生成したオブジェクトに重力をつけ検知した平面と衝突判定をつけてあげればこのようにテーブルから転げ落ちる表現も簡単に実装できます。

またStencil Shaderを使ったりして平面検知したPlaneの後ろに回ったオブジェクトは表示させないようにしたりすると綺麗に見えると思います。

Meshを更新した際にPrefabが壊れてしまう問題を解決する

Unityを使用しているときによく使う機能でPrefabというものがあります。
私も普段よくPrefabを利用していますが先日このような事が起こりました。

とあるMesh(TestModel.fbxとします)を参照しているPrefab(TestModel_Prefabとします)があり。
Meshにいくつかの修正を加えMeshを入れ替えるとPrefabの表示がとんでもない事になってしまいました。


このPrefabが参照しているMeshを更新します。
f:id:Cyario:20160831164521p:plain


すると、とんでもない事に....
f:id:Cyario:20160831164526p:plain



この時Meshの構造、ボーンの構造に特に変更はない簡単な変更のはずなのにこのような事態になりました。
Prefabの参照元のMeshを更新できないとなると毎回Prefabを作成しないといけなくなり、参照しているPrefabが多いと大変面倒でヒューマンエラーの原因にもなりかねないです。
なんとか解決しようと詳しく調べてみるとどうもMeshの頂点番号に変更があり、UnityではMeshのもつ情報からボーンとの接続を行うらしくそれが原因らしいと分かりました。

そして色々調べて試しにこのようなScriptを作成してみると

public class ResetBoneMap : MonoBehaviour
{
	public GameObject original;

	public void Convert()
	{
		SkinnedMeshRenderer[] skinnedMeshs = this.GetComponentsInChildren<SkinnedMeshRenderer>();

		foreach ( var s in skinnedMeshs )
		{
			Reset( s.GetComponent<SkinnedMeshRenderer>(), s.name );
		}
	}

	void Reset( SkinnedMeshRenderer targetRenderer, string name )
	{
		SkinnedMeshRenderer originalRenderer = getChildGameObject( original, name ).GetComponent<SkinnedMeshRenderer>();

		if (targetRenderer == null || originalRenderer == null ) return;

		Transform[] newBones = new Transform[targetRenderer.bones.Length];

		for ( int i = 0; i < originalRenderer.bones.Length; i++ )
		{
			for ( int j = 0; j < targetRenderer.bones.Length; j++ )
			{
				if ( targetRenderer.bones[j].name == originalRenderer.bones[i].name )
				{
					newBones[i] = targetRenderer.bones[j];
					continue;
				}
			}
		}

		targetRenderer.sharedMesh = originalRenderer.sharedMesh;
		targetRenderer.bones = newBones;		
	}

	GameObject getChildGameObject(GameObject fromGameObject, string withName)
	{
		Transform[] ts = fromGameObject.transform.GetComponentsInChildren<Transform>();
		foreach (Transform t in ts) if (t.gameObject.name == withName) return t.gameObject;
		return null;
	}
}

なんと・・うまく出力されました!!
f:id:Cyario:20160831170159p:plain


これでMeshの更新がなんとか楽になりそうです。


こちらのScriptはGithubにて公開しました。
github.com


使い方は簡単で壊れたPrefabにアタッチして元のMeshをOriginalに入れて・・・
f:id:Cyario:20160831164530p:plain


あとはボタンを押すだけ。
f:id:Cyario:20160831164534p:plain

処理が終われば綺麗な状態に直ります。

.unitypackageの格納場所

Asset Storeからダウンロードした展開前の.unitypackageがどこに格納されているのか気になって調べてみました。

Macでは下記に入っているみたいです。

~/Library/Unity/Asset Store
~/Library/Unity/Asset Store-5.x

Windowsでは確認していません