開発メモ」カテゴリーアーカイブ

開発技術に関する記事です。

[Unity, PlayMaker]:Androidビルドで勝手にパーミッションが追加される

検証バージョン:Unity 4.5.5f1, PlayMaker 1.7.7f6

PlayMakerをインポートしたプロジェクトをAndroid向けにビルドすると、以下の権限(パーミッション)が勝手に追加されます。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

Unity自体が、特定のAPIを使用した場合にビルド時に自動的に権限を追加する仕様になっています。
一旦Androidプロジェクトを吐き出してからビルドする場合は変更できますが、直接apkを書き出す場合は厄介です。

もし該当する機能を使っていないのならば、PlayMakerのアクションを記述してあるスクリプトファイルを最初に削除してしまえば済むことです。

・android.permission.INTERNET
/PlayMaker/Actions/Network フォルダごと削除

・android.permission.VIBRATE
/PlayMaker/Actions/DeviceVibrate.cs

・android.permission.ACCESS_FINE_LOCATION
/PlayMaker/Actions/GetLocationInfo.cs
/PlayMaker/Actions/StartLocationServiceUpdates.cs
/PlayMaker/Actions/StopLocationServiceUpdates.cs

・android.permission.WAKE_LOCK
/PlayMaker/Actions/DevicePlayFullScreenMovie.cs

[参考]:apkに付与されている権限を調べる方法
aaptというADT付属のツールで調べることが出来ます。
詳しくはこちらを参考にさせて頂きました。
Yukiの枝折 – 「apkからマニフェストの内容を読み取る方法」

[Unity]:PlayMaker カスタムアクション入門

PlayMakerではアクションという処理の単位を組合せて、処理を記述していきます。

PlayMaker公式アクションリファレンス(英語)

標準でもかなりの数のアクションが用意されていますが、実際にPlayMakerを使って作業していると標準のものでは処理を実現できそうもない場合が出てきます。
その場合はカスタムアクションを自分で作成して、標準のアクションと同様にPlayMakerで使用することができます。

ここではPlayMaker標準で用意されている「IntOperator」アクションを例にとり、アクションの記述の仕方を解説していきます。
「IntOperator」は2つのInt値と計算方法を指定し、その結果を指定のInt型変数に代入するアクションです。

namespace HutongGames.PlayMaker.Actions
{
	・・・
}

独自のネームスペースを使うことも可能ですが、特に理由が無い限りはHutongGames.PlayMaker.Actionsを使用するのが無難です。

namespace HutongGames.PlayMaker.Actions
{
	[ActionCategory(ActionCategory.Math)]
	[Tooltip("Performs math operation on 2 Integers: Add, Subtract, Multiply, Divide, Min, Max.")]
	public class IntOperator : FsmStateAction
	{
		・・・
	}
}

PlayMakerのアクションブラウザに表示される時のカテゴリを指定します。

PlayMakerActionBrowser

標準のカテゴリに追加することもできますし、独自のカテゴリに追加することもできます。
独自のカテゴリに追加したい場合は以下のようにカテゴリ名を文字列で指定します。

	[ActionCategory("CustomCategory")]
namespace HutongGames.PlayMaker.Actions
{
	[ActionCategory(ActionCategory.Math)]
	[Tooltip("Performs math operation on 2 Integers: Add, Subtract, Multiply, Divide, Min, Max.")]
	public class IntOperator : FsmStateAction
	{
		・・・
	}
}

アクションブラウザに表示するアクションの説明文を指定します。

PlayMakerActionBrowserPreview

省略しても構いません。

namespace HutongGames.PlayMaker.Actions
{
	[ActionCategory(ActionCategory.Math)]
	[Tooltip("Performs math operation on 2 Integers: Add, Subtract, Multiply, Divide, Min, Max.")]
	public class IntOperator : FsmStateAction
	{
		・・・
	}
}

HutongGames.PlayMaker.FsmStateActionを継承させます。

		[RequiredField]
		public FsmInt integer1;
		[RequiredField]
		public FsmInt integer2;
		public Operation operation = Operation.Add;
		[RequiredField]
		[UIHint(UIHint.Variable)]
		public FsmInt storeResult;
		public bool everyFrame;

PlayMakerIntOperator

PlayMakerエディタ上に表示されるパラメータとなります。
「RequiredField」属性を指定すると省略不可となり、エディタ上で適切な値を指定しない場合にエラーとなります。

「UIHint」属性を指定することで、エディタ上での表示の仕方を変えることができます。
「UIHint.Variable」を指定した場合には、FSMで用意した変数から選択することしかできなくなります。

メンバ変数の型を「Fsm〜」という型で指定することにより、エディタの機能を利用した便利な指定ができるようになります。
ここではFsmIntしか登場しませんが、他にFsmFloatやFsmStringなど様々な型が用意されています。

また、通情のC#の型も使用することができます。ここでは「everyFrame」がbool型で宣言されています。

		public override void Reset()
		{
			integer1 = null;
			integer2 = null;
			operation = Operation.Add;
			storeResult = null;
			everyFrame = false;
		}

新規にアクションを追加した場合の初期値、またはエディタ上でリセットをかけた場合にセットされる値を指定します。

		public override void OnEnter()
		{
			DoIntOperator();
			
			if (!everyFrame)
				Finish();
		}

アクションが実行される時に呼び出されます。
この中に直接処理を記述しても構いませんが、ここでは実際の処理は「DoIntOperator()」の中に記述されています。
処理の終了には必ず「Finish()」を呼び出す必要があります。
ただし、ここでは毎フレームの処理を行う場合は処理を終了させたく無いのでeneryFameフラグにより条件分岐されています。

		// NOTE: very frame rate dependent!
		public override void OnUpdate()
		{
			DoIntOperator();
		}

毎フレーム実行される処理を記述します。

		void DoIntOperator()
		{
			int v1 = integer1.Value;
			int v2 = integer2.Value;

			switch (operation)
			{
				case Operation.Add:
					storeResult.Value = v1 + v2;
					break;

				case Operation.Subtract:
					storeResult.Value = v1 - v2;
					break;

				case Operation.Multiply:
					storeResult.Value = v1 * v2;
					break;

				case Operation.Divide:
					storeResult.Value = v1 / v2;
					break;

				case Operation.Min:
					storeResult.Value = Mathf.Min(v1, v2);
					break;

				case Operation.Max:
					storeResult.Value = Mathf.Max(v1, v2);
					break;
			}
		}

「Fsm〜」型の変数はValueプロパティにより実際の値にアクセスすることができます。
注意点としてはin/outの区別が無いので、処理を記述する際に余計な値を変更してしまわないようにしましょう。

ーーーーー
以上のようにカスタムアクションはとても簡単に作成することができます。
GameObjectの参照をとったり、より複雑な処理をする場合は、標準のアクションの中から似たような処理をするものを見つけ、そのスクリプトを参考にすると良いでしょう。
アクションブラウザ上でアクションの右クリックメニューから簡単にスクリプトを参照することができます。

[Unity]:PlayMaker実践解説 (2)

今回はタイトル画面でスタートボタンが押された時の処理を解説していきます。

ArchuTitle ArchuTitleHierarchy2

○ StartButton
NGUIのUIButtonコンポーネントを持つボタンのGUI部品です。
NGUIのイベントをFSMに通知するためにPlayMakerの開発元から提供されているNGUIアドインをセットしてあります。

スタートボタンを押すと初回はプロローグ画面へ、その後はゲーム画面へ遷移するようになっています。

ArchuStartButtonFSM

ArchuStartButtonFSMEvent

ArchuStartButtonFSMVariable

1. Init
シーンが読込まれPlayMakerFSMが動き出した時に初めに到達する状態(ステート)です。
前回紹介したTitleControllerのInitとはほぼ同時(順不同)で動作します。
UnityのスクリプトにおけるAwake()の実行順と同じとイメージすれば良いでしょう。
ただしPlayMakerの場合は実行順を制御する機構がないようですので、FSM同士の関連には注意が必要です。
(※:未確認ですがUnityのスクリプトの実行順制御によりPlayMakerFSMの実行順も制御できるかもしれません。PlayMakerFSM自体も一種のコンポーネントですので。)

ArchuStartButtonInit

1-1. Stop Activity Indicator(カスタム)
処理中を示すインジケーター(くるくる回転するアイコン)を停止・非表示にします。

2. Idle
何も行いません。
ボタンが押下された時に「NGUI / ON CLICK」イベントが来るのを待ち受けます。

ArchuStartButtonIdle

3. OnClick
Idleの状態からボタンが押下された時に遷移する状態です。

ArchuStartButtonOnClick

3-1. Stop BGM(カスタム)
再生中のBGMを停止します。

3-2. Play Start Button SE(カスタム)
スタートボタン押下の効果音を再生します。

4. CheckPrologue
スタートボタン押下後に到達するステートです。
初回起動かどうかを判断して、プロローグかゲーム画面に遷移を振り分けます。
上記のOnClick中に書いても良い処理ですが、分かり易さのために分けてあります。

ArchuStartButtonCheckPrologue

4-1. PlayerPrefs Get Int(PlayerPrefs)
PlayerPrefに保存されている物語の進行度を表す数値を取り出し、変数storyにセットします。
Unity標準のPlayerPrefs.GetInt()と同様のアクションですが、複数の値を同時に扱えるのが少し便利です。

4-2. Int Compare(Logic)
さきほど取り出したstoryの値が1以上なら「START_GAME」イベントを発生しゲーム画面へ遷移する処理へ、1未満なら「FINISHED」イベントを発生しプロローグへ遷移する処理へ移ります。

5. StartPrologue
初めてスタートボタンを押した時に到達するステートです。

ArchuStartButtonStartPrologue

5-1. Activate Game Object(GameObject)
インスペクタ上で指定したフェードアウト用のNGUIスプライトを有効にします。
Unity標準のGameObject.SetActive()と同様のアクションです。

5-2. Wait(Time)
1.1秒間待機した後に指定のイベント(今回は指定してないのでFINISHEDイベント)を発生します。
このように待機した後に処理を行いたい場合は、別の状態を用意してイベントにより遷移させる必要があります。
このアクションが途中に挟まれていたとしても、以降のアクションも全て実行される点に注意が必要です。

6. LoadLevelPrologue
プロローグ画面へ遷移します。

ArchuStartButtonLoadLevelPrologue

6-1. Move Off Ad Banner(カスタム)
広告バナーを画面外に隠します。

6-2. Load Level(Level)
「Prologue」という名前のシーンをロードします。
UnityのApplication.LoadLevel()にあたるアクションですが、Additive/Asyncなどがオプションとして選択できるようになっています。

7. StartGame
すでにゲームをやったことがある場合にスタートボタンを押すと到達するステートです。
内容は前述の「5. StartPrologue」とほぼ同様なので説明は省略します。

ArchuStartButtonStartGame

8. LoadLevelGame
ゲーム画面へ遷移します。
さきほどのプロローグ画面への遷移と違いロード中画面を挟んだ遷移なので、アクションの内容が異なっています。

ArchuStartButtonLoadLevelGame

6-1. Play Game BGM(カスタム)
ゲーム画面用のBGMの再生を開始します。

6-2. Load Lovel Transition(カスタム)
ロード中画面を表示した上で「Stage01」という名前のシーンをロードします。

ーーーーー
今回の中では「2. Idle」の何もアクションの無い状態が特徴的だったと思います。
こういう状態を用意することで、外部からの操作でイベントを飛ばしてもらって処理を行うというような記述が可能になります。

また、プロローグの場合とゲーム画面の場合でフェードアウトの処理を変えていたり、BGMを止めたり、ロード中画面を挟んだりなど微妙な調整を色々としています。
これがどのような違いを生むかは、もうじきリリース予定の「あ〜ちゃ〜の大冒険」でぜひ確認してみて欲しいと思います。

[Unity]:PlayMaker実践解説 (1)

スマホアプリ「あ〜ちゃ〜の大冒険」(2014年12月下旬リリース予定)は、実験的にPlayMakerというコーディング無しで処理を記述できるアセットを全面的に利用して作りました。
そこで実際にPlayMakerを使ってどのように処理を記述しているかを公開します。

PlayMaker自体の詳細に関しては、公式の紹介動画・チュートリアル動画で概要は掴んで頂けると思います。
また少ないながらも日本語で解説されているサイトもありますのでそちらを参照して下さい。

PlayMaker
公式サイト
アセットストア

初回は、小手調べということでアプリのタイトル画面の初期化を管理しているFSM(PlayMakerの処理内容を記述したもの)をご紹介します。

タイトル画面
ArchuTitle TitleControllerHierarchy

○ TitleController
ここでは広告の初期化、またアプリ内で唯一となる音源を管理するゲームオブジェクトAudioManagerを作成しBGMを再生します。

TitleControllerFSM

TitleControllerEvent

TitleControllerVariable

1. Init
シーンが読込まれPlayMakerFSMが動き出した時に初めに到達する状態(ステート)です。

TitleControllerInit

1-1. Init Ad Banner(カスタム)
自分で作成したカスタムアクションでiOS/Androidそれぞれのネイティブプラグインを呼び出し、広告バナーの初期化・表示を行います。
カスタムアクションに関しては下記を参考にして下さい。

 [Unity]:PlayMaker カスタムアクション入門

1-2. Find Game Object(GameObjectカテゴリ)←括弧内はPlayMakerで用意されたアクションのカテゴリを示します。
「AudioManager」というタグが付けられたGameObjectを取得し、変数audioManagerにセットします。
存在しなければnullになります。

1-3. Game Object Is Null(Logic)
audioManagerがnullかどうかを調べます。
nullの場合は「NO_AUDIO_MANAGER」というイベントを発生し遷移、nullでない場合は通常終了し「FINISHED」イベントによる遷移を行います。

2. CreateAudioManager
InitでAudioManagerが存在しなかった場合に到達するステートです。
このステートでは特に遷移イベントを指定していませんが、その場合は全てのアクション実行後に自動的にFINISHEDイベントによる遷移が行われます。

TitleControllerCreateAudioManager

2-1. Create Object(GameObject)
インスペクタで指定してあるAudioManagerのプレファブからインスタンスを作成し、変数audioManagerにセットします。

2-2. Dont Destroy On Load(Level)
audioManagerがシーンロード時に破棄されないように設定します。

3. PlayTitleBGM
タイトル画面のBGMを再生します。

TitleControllerPlayTitleBGM

3-1. Play Title BGM(カスタム)
AudioManagerの機能を利用してタイトル画面のBGMを再生するカスタムアクションです。

ーーーーー
状態(ステート)が3つ、それぞれのアクションも少ないですが、PlayMakerによる実装の感じは掴んで頂けたと思います。
状態遷移というよりは処理の固まりを遷移していくようなイメージになっているのは私のクセという部分もあります。

変数がFSM単位で用意され、スクリプトでいうところのメソッドのローカル変数のようなものが存在しないのが特徴と言えます。
また条件分岐の場合には必ず状態遷移を引き起こすというのもクセのあるところです。

今回、これだけ少ないアクションの中で2ヶ所もカスタムアクションが登場しましたが、少し込み入ったことをしようと思うとカスタムアクションを作成した方が楽です。
PlayMaker標準でMonoBehaviourのメソッドを呼び出すアクションが存在しますが、上手く利用出来ませんでした。

次回はタイトル画面でスタートボタンを押した時のFSMを紹介する予定です。

[Unity]:GenymotionでAndroidアプリを動作させる

GenymotionとはAndroid端末のエミュレータです。
Addroid端末のエミュレータはAndroid Development Tools(ADT)にも標準で添付されていますが、もの凄く動作が遅いです。それに比べ、このGenymotionはとても快適に動作します。

Unityで作成したAndroidアプリをGenymotionで動作させる際に少しハマったので、簡単にまとめておきます。

Genymotionの導入に関しては、解説されている日本語サイトもたくさんありますのでそちらを参照して下さい。
Macの場合、Genymotionを動作させるためのVirtual Boxというソフトを予めインストールしておく必要があります。(Windowsの場合はVirtual Box同梱のGenymotionインストーラがあります)
Virtual Boxのダウンロードが少し分かりにくくて(私だけかも知れません^^;)、Mac用の記述が見つかりませんが「for OS X hosts」というのがそれに当たります。

GenymotionでAndroid端末のエミュレータが動作しても、そのままではUnityで作成したAndroidアプリのapkを実行することができません。(使用PCのCPUに依ると思われます)
Unityから吐き出されるapkはARMv7 onlyとなっており、私のIntel Macで動作するGenymotionでは実行出来ませんでした。

そこでARM TranslationというモジュールをGenymotionの仮想端末に導入します。
これに関しても解説されているサイトがたくさんありますので、そちらを参照下さい。
注意点としては(これも私だけかも知れませんが^^;)、SafariでARM Translationのモジュールをダウンロードするとzipが自動的に解凍されてしまいます。
Genymotionに適用するにはzipのままでないといけないので、Safariの設定を変更して、zipを自動的に解凍しないようにしておく必要があります。

[Unity]:iOSシミュレータでネイティブプラグインを動かす

UnityからiOSビルドをする時に、「PlayerSettings」-「Other Settings」-「Optimization」-「SDK Version」をデフォルトの「Device SDK」から「Simulator SDK」に変更する事により、iOS Simulator用のXcodeプロジェクトを吐き出し、iOS Simulator上で動作を確認することができます。

ただUnityのiOS Simulator向けビルドはネイティブプラグインの呼び出しをサポートしておらず、ネイティブプラグイン呼び出し時に以下ようなのエラーを吐いて、動作しません。

EntryPointNotFoundException: initAdBanner_
  at (wrapper managed-to-native) Binding:initAdBanner_ (string)
  at Binding.initAdBanner () [0x00000] in <filename unknown>:0

このエラーを回避し、ネイティブプラグインをシミュレータ上で動作させる方法がこちらのサイトで紹介されていました。

「Jason Lu Game Programmer –
Unity4: Test Unity iOS plug-in on iOS simulator」

画像付きですし分かり易いと思いますが、日本語で分かり易く紹介しておきます。
Unityのバージョンは4.5.5f1で試しています。

変更はUnityからiOS Simulatorビルドで吐き出されたXcodeプロジェクトの以下のソース中の2カ所に対して行います。

 Librariesフォルダ – RegisterMonoModules.cpp

1.mono_dl_register_symbolの宣言を#ifディレクティブの外に出します。

extern "C"
{
	typedef void* gpointer;
	typedef int gboolean;
#if !(TARGET_IPHONE_SIMULATOR)
	const char*			UnityIPhoneRuntimeVersion = "4.5.5f1";
	void				mono_dl_register_symbol (const char* name, void *addr);
	extern int 			mono_ficall_flag;
	void				mono_aot_register_module(gpointer *aot_info);
	extern gboolean		mono_aot_only;
	extern gpointer*	mono_aot_module_Assembly_CSharp_firstpass_info; // Assembly-CSharp-firstpass.dll
	extern gpointer*	mono_aot_module_Assembly_CSharp_info; // Assembly-CSharp.dll
	extern gpointer*	mono_aot_module_Assembly_UnityScript_info; // Assembly-UnityScript.dll
	extern gpointer*	mono_aot_module_Boo_Lang_info; // Boo.Lang.dll
	extern gpointer*	mono_aot_module_Mono_Security_info; // Mono.Security.dll
	extern gpointer*	mono_aot_module_PlayMaker_info; // PlayMaker.dll
	extern gpointer*	mono_aot_module_System_Core_info; // System.Core.dll
	extern gpointer*	mono_aot_module_System_info; // System.dll
	extern gpointer*	mono_aot_module_UnityEngine_info; // UnityEngine.dll
	extern gpointer*	mono_aot_module_UnityScript_Lang_info; // UnityScript.Lang.dll
	extern gpointer*	mono_aot_module_mscorlib_info; // mscorlib.dll
#endif // !(TARGET_IPHONE_SIMULATOR)
	void	initAdBanner_();
	void	moveOffBanners_();
	void	share_();
	void	shareWithImage_();
	void	showAppStore_();
	void	UnityNSObject_RetainObject();
	void	UnityNSObject_ReleaseObject();
	void	UnityNSError_Code();
	void	UnityNSError_Description();
	void	UnityNSError_Reason();
	void	UnityNSNotification_Name();
}
extern "C"
{
	typedef void* gpointer;
	typedef int gboolean;
#if !(TARGET_IPHONE_SIMULATOR)
	const char*			UnityIPhoneRuntimeVersion = "4.5.5f1";
	extern int 			mono_ficall_flag;
	void				mono_aot_register_module(gpointer *aot_info);
	extern gboolean		mono_aot_only;
	extern gpointer*	mono_aot_module_Assembly_CSharp_firstpass_info; // Assembly-CSharp-firstpass.dll
	extern gpointer*	mono_aot_module_Assembly_CSharp_info; // Assembly-CSharp.dll
	extern gpointer*	mono_aot_module_Assembly_UnityScript_info; // Assembly-UnityScript.dll
	extern gpointer*	mono_aot_module_Boo_Lang_info; // Boo.Lang.dll
	extern gpointer*	mono_aot_module_Mono_Security_info; // Mono.Security.dll
	extern gpointer*	mono_aot_module_PlayMaker_info; // PlayMaker.dll
	extern gpointer*	mono_aot_module_System_Core_info; // System.Core.dll
	extern gpointer*	mono_aot_module_System_info; // System.dll
	extern gpointer*	mono_aot_module_UnityEngine_info; // UnityEngine.dll
	extern gpointer*	mono_aot_module_UnityScript_Lang_info; // UnityScript.Lang.dll
	extern gpointer*	mono_aot_module_mscorlib_info; // mscorlib.dll
#endif // !(TARGET_IPHONE_SIMULATOR)
	void	mono_dl_register_symbol (const char* name, void *addr);
	void	initAdBanner_();
	void	moveOffBanners_();
	void	share_();
	void	shareWithImage_();
	void	showAppStore_();
	void	UnityNSObject_RetainObject();
	void	UnityNSObject_ReleaseObject();
	void	UnityNSError_Code();
	void	UnityNSError_Description();
	void	UnityNSError_Reason();
	void	UnityNSNotification_Name();
}

2.RegisterMonoModules()中のネイティブ呼び出しするメソッドの定義を#ifディレクティブから外す

void RegisterMonoModules()
{
    gEnableGyroscope = false;
#if !(TARGET_IPHONE_SIMULATOR)
	mono_aot_only = true;
	mono_ficall_flag = true;
	mono_aot_register_module(mono_aot_module_Assembly_CSharp_firstpass_info);
	mono_aot_register_module(mono_aot_module_Assembly_CSharp_info);
	mono_aot_register_module(mono_aot_module_Assembly_UnityScript_info);
	mono_aot_register_module(mono_aot_module_Boo_Lang_info);
	mono_aot_register_module(mono_aot_module_Mono_Security_info);
	mono_aot_register_module(mono_aot_module_PlayMaker_info);
	mono_aot_register_module(mono_aot_module_System_Core_info);
	mono_aot_register_module(mono_aot_module_System_info);
	mono_aot_register_module(mono_aot_module_UnityEngine_info);
	mono_aot_register_module(mono_aot_module_UnityScript_Lang_info);
	mono_aot_register_module(mono_aot_module_mscorlib_info);

	mono_dl_register_symbol("initAdBanner_", (void*)&initAdBanner_);
	mono_dl_register_symbol("moveOffBanners_", (void*)&moveOffBanners_);
	mono_dl_register_symbol("share_", (void*)&share_);
	mono_dl_register_symbol("shareWithImage_", (void*)&shareWithImage_);
	mono_dl_register_symbol("showAppStore_", (void*)&showAppStore_);
	mono_dl_register_symbol("UnityNSObject_RetainObject", (void*)&UnityNSObject_RetainObject);
	mono_dl_register_symbol("UnityNSObject_ReleaseObject", (void*)&UnityNSObject_ReleaseObject);
	mono_dl_register_symbol("UnityNSError_Code", (void*)&UnityNSError_Code);
	mono_dl_register_symbol("UnityNSError_Description", (void*)&UnityNSError_Description);
	mono_dl_register_symbol("UnityNSError_Reason", (void*)&UnityNSError_Reason);
	mono_dl_register_symbol("UnityNSNotification_Name", (void*)&UnityNSNotification_Name);
#endif // !(TARGET_IPHONE_SIMULATOR)
}
void RegisterMonoModules()
{
    gEnableGyroscope = false;
#if !(TARGET_IPHONE_SIMULATOR)
	mono_aot_only = true;
	mono_ficall_flag = true;
	mono_aot_register_module(mono_aot_module_Assembly_CSharp_firstpass_info);
	mono_aot_register_module(mono_aot_module_Assembly_CSharp_info);
	mono_aot_register_module(mono_aot_module_Assembly_UnityScript_info);
	mono_aot_register_module(mono_aot_module_Boo_Lang_info);
	mono_aot_register_module(mono_aot_module_Mono_Security_info);
	mono_aot_register_module(mono_aot_module_PlayMaker_info);
	mono_aot_register_module(mono_aot_module_System_Core_info);
	mono_aot_register_module(mono_aot_module_System_info);
	mono_aot_register_module(mono_aot_module_UnityEngine_info);
	mono_aot_register_module(mono_aot_module_UnityScript_Lang_info);
	mono_aot_register_module(mono_aot_module_mscorlib_info);
#endif // !(TARGET_IPHONE_SIMULATOR)

	mono_dl_register_symbol("initAdBanner_", (void*)&initAdBanner_);
	mono_dl_register_symbol("moveOffBanners_", (void*)&moveOffBanners_);
	mono_dl_register_symbol("share_", (void*)&share_);
	mono_dl_register_symbol("shareWithImage_", (void*)&shareWithImage_);
	mono_dl_register_symbol("showAppStore_", (void*)&showAppStore_);
	mono_dl_register_symbol("UnityNSObject_RetainObject", (void*)&UnityNSObject_RetainObject);
	mono_dl_register_symbol("UnityNSObject_ReleaseObject", (void*)&UnityNSObject_ReleaseObject);
	mono_dl_register_symbol("UnityNSError_Code", (void*)&UnityNSError_Code);
	mono_dl_register_symbol("UnityNSError_Description", (void*)&UnityNSError_Description);
	mono_dl_register_symbol("UnityNSError_Reason", (void*)&UnityNSError_Reason);
	mono_dl_register_symbol("UnityNSNotification_Name", (void*)&UnityNSNotification_Name);
}

以上の2カ所を修正することにより、ネイティブプラグインがiOSシミュレータ上でも動作するようになります。

[Unity]:ProBuilder2.0

Unityエディタ上でモデルのメッシュをいじって簡単なモデリングが出来るツールです。

イメージを掴むためにこの動画をお勧めします。

ProBuilder v2.2 Overview

英語だし、音量が小さいですが、眺めるだけで良いです。すごくワクワクしますよ^^

同社のPrototypeというツールはProBuilderの下位版という位置づけでいくらか制限があるようですが、2014年9月いっぱいは無料セールをやっているので是非試してみると良いでしょう。

またProCore Complete Bundleというバンドル製品には、ProBuilder以外に便利なツールが含まれており、特にProGridというツールはProBuilderで準必須となっているので、購入する場合はこちらの検討をした方が良いと思います。

今回はProCore Complete Bundleを購入した場合のインストールと編集モードの説明をします。

もっと踏み込んだ説明に関しては未定です。ご要望あれば。 (→ @mokusan
私もProCoreを購入してサラッとドキュメントを見ながら触ってみただけなのですが、分かることにはお応えしたいと思います。

ProCoreをダウンロードしてインポートすると「ProCoreCompleteBundle」というフォルダの下に各ツールのunitypackageと各種ドキュメントがあるので、その中の「ProBuilder2-v〜」というパッケージをダブルクリックしてインストールします。

ProCore

インストール時にRelease版かSource版か選択できます。
サンプルやActionツール(モデルやフェースの変換などの便利ツール)を使う場合はSource版が必要とのことですが、その代わりにスクリプトのコンパイル時に少し処理が重くなります。

ProBuilderInstall

この時、「ProGrids2-v〜」も一緒にインストールしておくと良いでしょう。

インストールしたら先ずはUnityのメニューバーから「Tools – ProBuilder – ProBuilder Window」を選択してProBuilderを有効にします。

ProBuilderMenu

するとProBuilderのツールバーのフローティングウィンドウが表示され、Sceneビューには編集モード選択のためのボタンが表示されます。

ProBuilderWindowProBuilderModeButton

・編集モード
3つの編集モードがあり、ProBuilderのツールバーのアイコンをクリック、シーンビューのボタンをクリックもしくはショートカットキーで切り替えが可能です。
シーンビューのボタンの「Top」はObject Editingモードを指します。

ショートカットキー:
h(Object EditingとFace Geometry Editingの切り替え)
j(Texture Editingに切り替え)
esc(Object Editingに戻る)

ProBuilderObjectEditingMode Object Editing
通常のUnityのエディタと同様にオブジェクトの選択、移動などができます。

ProBuilderObjectEditing

FaceModeVertextModeEdge Geometry Editing
メッシュデータの編集ができます。
Blenderなどの3Dモデリングツールを使ったことがあれば分かり易いと思います。
Geometry Editingにはさらに3つの選択モードがあり、ProBuilderツールバーのアイコンクリックもしくはショートカットキー’g’で切り替えが出来ます。

– Face Geometry Editing FaceMode
face(面)を選択・編集できます。
ProBuilderFaceEditing

– Vertex Geometry Editing VertextMode
vertex(頂点)を選択・編集できます。
ProBuilerVertexEditing

– Edge Geometry Editing Edge
edge(辺)を選択・編集出来ます。(β版)
ProBuilderEdgeEditing

TextureMode Texture Editing
テクスチャの貼付・設定ができます。
厳密に言うとマテリアルの貼付けなので、テクスチャなどを設定したマテリアルを予め用意しておく必要があります。

ProBuilderTextureEditing

今回はここまで。
最初の方にも書きましたが、もっと踏み込んだ説明に関しては未定です。
ご要望あれば。 (→ @mokusan

[Unity]:[エディタ拡張]:SceneにGameObjectを大量に配置する

使用バージョン:Unity 4.5.2f1

製作中のゲームで海面に大量のブイを並べて配置したいのですが、手作業ではとても無理なのでエディタ拡張を利用して、一定間隔でGameObjectを複製して配置する機能を作ってみました。

WaterBoat画面イメージ

初めてのエディタ拡張で間違ってる部分もありそうなので、ご意見など頂けたら幸いです。(@mokusan)

先ず完成イメージは以下。

CreateBuoys完成イメージ01

このように必要なパラメータを設定して「Create」を押下すると、先に挙げたゲーム画面のようにブイが一定間隔で配置される。

・エディタ拡張用のスクリプト
「Editor」という名前のフォルダ配下に置く必要がある。
Resourcesと同様で他のフォルダを掘って、その下に「Editor」フォルダを配置しても大丈夫。
「Editor」フォルダ
(スクリプト”CreateBuoys.cs”の内容全文はこの投稿の最後に掲載しておきます。)

・UnityEditor名前空間の型の使用を許可
通常のUnityEngine以外にUnityEditorを指定しておく。

using UnityEngine;
using UnityEditor;
using System.Collections;

・メニューアイテムの追加
メニューアイテム01 メニューアイテム02

[MenuItem("GameObject/Create Other/Create Buoys")]
static void Init() {
	EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys");
}

“GameObject/Create Other/”配下を指定することで、Hierarchyビューの「Create」のメニューにも追加される。

・ウィンドウの表示
メニューから「Create Buoys」が選択された時に専用のウィンドウを表示する。

[MenuItem("GameObject/Create Other/Create Buoys")]
static void Init() {
	EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys");
}

EditorWindow.GetWindow

・ウィンドウ内容の表示
必要なメンバ変数を定義し、OnGUI()内で記述する。

public class CreateBuoys : EditorWindow {
	private GameObject parent;
	private GameObject prefab;
	private int numX = 1;
	private int numY = 1;
	private int numZ = 1;
	private float intervalX = 1;
	private float intervalY = 1;
	private float intervalZ = 1;
void OnGUI() {
	try {
		parent = EditorGUILayout.ObjectField("Parent", parent, typeof(GameObject), true) as GameObject;
		prefab = EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), true) as GameObject;

		GUILayout.Label("X : ", EditorStyles.boldLabel);
		numX = int.Parse(EditorGUILayout.TextField("num", numX.ToString()));
		intervalX = int.Parse(EditorGUILayout.TextField("interval", intervalX.ToString()));

		GUILayout.Label("Y : ", EditorStyles.boldLabel);
		numY = int.Parse(EditorGUILayout.TextField("num", numY.ToString()));
		intervalY = int.Parse(EditorGUILayout.TextField("interval", intervalY.ToString()));

		GUILayout.Label("Z : ", EditorStyles.boldLabel);
		numZ = int.Parse(EditorGUILayout.TextField("num", numZ.ToString()));
		intervalZ = int.Parse(EditorGUILayout.TextField("interval", intervalZ.ToString()));

		GUILayout.Label("", EditorStyles.boldLabel);
		if (GUILayout.Button("Create")) Create();
	} catch (System.FormatException) {}
}

通常のOnGUI()でGUIを作るのと同じ要領だが、EditorGUILayoutのパーツが使える。
詳細はリファレンスを参照。
GUILayout
EditorGUILayout

・ウィンドウ表示開始時に選択していたオブジェクトを取得
オブジェクトを取得01

void OnEnable() {
	if (Selection.gameObjects.Length > 0) parent = Selection.gameObjects[0];
}

ウィンドウ表示時に自動で呼ばれるメソッド。
Selection.gameObjects

・ウィンドウ表示後に選択したオブジェクトを反映
オブジェクトを取得02

void OnSelectionChange() {
	if (Selection.gameObjects.Length > 0) prefab = Selection.gameObjects[0];
	Repaint();
}

選択内容の変更時に自動で呼ばれるメソッド。

・「Create」ボタン押下時に実際にブイを作成する

private void Create() {
	if (prefab == null) return;

	int count = 0;
	Vector3 pos;

	pos.x = -(numX - 1) * intervalX / 2;
	for (int x = 0; x < numX; x++) {
		pos.y = -(numY - 1) * intervalY / 2;
		for (int y = 0; y < numY; y++) {
			pos.z = -(numZ - 1) * intervalZ / 2;
			for (int z = 0; z < numZ; z++) {
				GameObject obj = Instantiate(prefab, pos, Quaternion.identity) as GameObject;
				obj.name = prefab.name + count++;
				if (parent) obj.transform.parent = parent.transform;
				Undo.RegisterCreatedObjectUndo(obj, "Create Buoys");

				pos.z += intervalZ;
			}
			pos.y += intervalY;
		}
		pos.x += intervalX;
	}
}

普通にInstantiate()すれば、Hierarchyに追加される。
この際、Undoを設定しておくことにより、後で取り消しができる。(66行目)

Editor拡張を利用したのは初めてだが、食わず嫌いだったと反省している。
Unityで新規にGameObjectを作った時にHierarchyのトップに作成されてしまうというのは、良く聞く不満だが、Editor拡張を使えばその辺りも何とかできそうだと言うのが分かった。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class CreateBuoys : EditorWindow {
	private GameObject parent;
	private GameObject prefab;
	private int numX = 1;
	private int numY = 1;
	private int numZ = 1;
	private float intervalX = 1;
	private float intervalY = 1;
	private float intervalZ = 1;

	[MenuItem("GameObject/Create Other/Create Buoys")]
	static void Init() {
		EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys");
	}

	void OnEnable() {
		if (Selection.gameObjects.Length > 0) parent = Selection.gameObjects[0];
	}

	void OnSelectionChange() {
		if (Selection.gameObjects.Length > 0) prefab = Selection.gameObjects[0];
		Repaint();
	}

	void OnGUI() {
		try {
			parent = EditorGUILayout.ObjectField("Parent", parent, typeof(GameObject), true) as GameObject;
			prefab = EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), true) as GameObject;

			GUILayout.Label("X : ", EditorStyles.boldLabel);
			numX = int.Parse(EditorGUILayout.TextField("num", numX.ToString()));
			intervalX = int.Parse(EditorGUILayout.TextField("interval", intervalX.ToString()));

			GUILayout.Label("Y : ", EditorStyles.boldLabel);
			numY = int.Parse(EditorGUILayout.TextField("num", numY.ToString()));
			intervalY = int.Parse(EditorGUILayout.TextField("interval", intervalY.ToString()));

			GUILayout.Label("Z : ", EditorStyles.boldLabel);
			numZ = int.Parse(EditorGUILayout.TextField("num", numZ.ToString()));
			intervalZ = int.Parse(EditorGUILayout.TextField("interval", intervalZ.ToString()));

			GUILayout.Label("", EditorStyles.boldLabel);
			if (GUILayout.Button("Create")) Create();
		} catch (System.FormatException) {}
	}

	private void Create() {
		if (prefab == null) return;

		int count = 0;
		Vector3 pos;

		pos.x = -(numX - 1) * intervalX / 2;
		for (int x = 0; x < numX; x++) {
			pos.y = -(numY - 1) * intervalY / 2;
			for (int y = 0; y < numY; y++) {
				pos.z = -(numZ - 1) * intervalZ / 2;
				for (int z = 0; z < numZ; z++) {
					GameObject obj = Instantiate(prefab, pos, Quaternion.identity) as GameObject;
					obj.name = prefab.name + count++;
					if (parent) obj.transform.parent = parent.transform;
					Undo.RegisterCreatedObjectUndo(obj, "Create Buoys");

					pos.z += intervalZ;
				}
				pos.y += intervalY;
			}
			pos.x += intervalX;
		}
	}
}

[Mac]:iPhoneのバックアップに失敗する→バックアップの削除方法

※ この方法で何らかの問題が発生しても当方では責任を負いかねますので、各自の責任の元に実行して頂きますようお願い致します。

数日前からiPhoneのバックアップが出来ずに、以下のエラーが表示されるようになりました。

iPhoneバックアップエラー

バックアップはiTunesの「環境設定…」を開き、「デバイス」タブから削除できます。

iPhoneバックアップ削除

バックアップを一旦削除した後は、正常にバックアップを取れるようになりましたが、削除する前に念のため元のバックアップファイルの退避を行っておいた方が良いでしょう。

[Unity]:スクリプトからシェーダの変数にアクセスする

先ほどのエントリで、ノーマルマップのオフセットにアクセスする方法でつまづいたので追記。

using UnityEngine;
using System.Collections;

public class Water : MonoBehaviour {
	void Update() {
		Vector2 offset = renderer.sharedMaterial.mainTextureOffset;
		offset.x = Mathf.Sin(Time.time * 2) * 0.01f;
		offset.y = Mathf.Repeat(offset.y - 0.001f, 1.0f);
		renderer.sharedMaterial.mainTextureOffset = offset;
		renderer.sharedMaterial.SetTextureOffset("_BumpMap", offset);
	}
}

マテリアルInspector

マテリアルのInspectorの「Shader」右側にある「Edit…」を押下。
※ ↑組み込みシェーダの場合。
それ意外の場合はエディタでシェーダのソースが表示される。
組み込みシェーダと同様にInspectorで一覧を確認したい場合は、Inspector右上の歯車アイコンから「Select Shader」を選択。

ShaderInspector

「Properties:」の中に「_BumpMap Texture: Normalmap」の表記があり、ノーマルマップにアクセスするためには「_BumpMap」という変数名を使用すれば良いことが判る。