読者です 読者をやめる 読者になる 読者になる

N煎ログブログ

n番煎じと言っても過言ではない今更な、でも個人的に躓いたUnityでの開発についての云々を書いていきます

【Unity5】ミサイルを簡単に作れるアセットを開発しました

ミサイル...いいですよね。

ゲーム作ってると、ミサイルとは言わなくとも何かしら飛翔する物体はあるもので、それらの挙動をいちいちプロジェクトごとに作り直したり、また微妙に違う挙動をさせたかったりすると色々と手間がかかります。

そこでこんなものを作ってみました(公開自体は5月ごろ)。

AdvancedMissile - Asset Store

これは主にミサイルの挙動、もしくはその他飛翔関連などの挙動を簡潔に設定可能なアセットです。

設定項目

現バージョン(Ver2.2.1)で設定可能な項目としては、

  • 死亡時間
  • 自然落下
  • コリジョン
  • ターゲット検索
  • 障害物回避
  • ターゲット座標、軌道ズレ
  • 移動方法、速度、角度
  • 効果音
  • エフェクト
  • 衝突後イベント発火

の計10項目。

動画

実際に動かしているのがこちら(AssetStore内動画より)


AdvancedMissile2.2.1 DEMO for Unity3D


AdvancedMissile2.2.1 Samples for Unity3D

エディタ拡張を活用した見た目の最適化

設定は項目ごとに階層構造となっており区分けされているので見た目的にもすっきり。

 

 gyazo.com

 

gyazo.com

まだ修正の余地がたくさんありますが結構自由に動きを設定できるので汎用性は高いと自負しています。

余談

余談ではありますが、ミサイル処理の実装方法について、こちらで解説を行っています。

www.nicovideo.jp

更新予定

今後の更新予定としては、

を追加予定。

アルゴリズム

二つ目の動画内で紹介しているAvoidance Missileのアルゴリズムについて書きました。

isemito.hatenablog.com

【Unity5】エディタ開発(拡張でない)において注意すべき事

皆さんエディタ拡張はしているでしょうか。

しかし今回ここで話すのはエディタ「拡張」ではなく「開発」についてです。

具体的には、エディタ上で編集した値の保存についてです。

 全ての総称として「拡張」と呼ばれていますが、わかりにくいので、私的な区別ですが「拡張」は既にあるUI上の見た目等を変更、改変するのを拡張と呼び、それに比べ、新規にウィンドウやUIを配置して自作のツールを制作するのが「開発」だという体でここでは述べていきますのであしからず。

また、Hierarchy上やProject上のアセットやオブジェクトを直接弄るようなものではなく、エディタ内部の値を保存させる方法について話していきます。

シリアライズ

Unity上では値をシリアライズすることによってその値を永久なものにするようになっています。

具体的には、以下の書き方で変数をシリアライズします。

public int hoge1;

もしくは

[SerializeField]

int hoge2;

publicな変数は自動的にシリアライズされるため、なにも考えなくても良いですが、publicをつけていない、もしくはprivateな変数の場合、「[SerializeField]」を変数の上行へ付ければシリアライズされるようになります。

シリアライズしていないとどうなるか?

シリアライズしていない変数の場合、Unityの再生ボタンを押した際に値が初期化(nullに)されます。これは、Unityがエディタを再生させた際にシーンをロードしているためだそう。

そのため、privateな変数等のシリアライズされていない変数は、Start関数やAwake関数で初期化するか、null判定するなどして対処しなければ、思わぬところでnullエラーを吐かれることになります。

ところがどっこいおもわぬ罠

 さて、これで自作エディタで色々した後、再生すればエディタ内部で編集した値が保存されると思いきや、そうはなりません。

Editor,EditorWindowを継承しているクラス上の変数は、publicだろうが[SerialiseField]を付けていようがお構いなしに値を初期化してくれやがります。

Hierarchyに置かれていなければ保存されない

 実は、publicや[SerialiseField]を使用してシリアライズして値を保存してくれるのは、Hierarchyに置かれているオブジェクトのコンポーネントのみです。細かくいえば、MonobehaviourやScriptableObjectを継承しているクラスです。

つまり、EditorやEditorWindowを継承しているクラスはどうあがこうがそのクラス内部の値は初期化されてしまいます(Hierarchyにも置けない)。

さらに、例えMonobehaviourを継承していても、やはりHierarchyに置かれていなければ、どうやら初期化されてしまうようです。

エディタ開発で何が困るの?

さて、これらを述べたうえで、値の初期化がなぜ困るのか。

一番困るのは、Hierarchy上やProject上のアセットやオブジェクトを一時的に弄り完結するようなエディタではなく、エディタ内部で値をUnityの再生/停止を跨いで長期的に扱う必要のあるエディタです。

例えば、わかりやすいのが、よくある状態遷移エディタあたりでしょうか。

あれは(アセットによりますが)再生する前にエディタ上で「ノード」と呼ばれる矩形のUIを配置して、それらを線でつなぎ、再生時の動作を作成/制御するというものですUE4(UnrealEngine4)のBP(BluePrint)がそれです。

しかし、エディタで作成したノードや線の情報をEditor/EditorWindow継承のクラスで管理すると、Unityを再生した際に初期化されてnullになり、画面は消え、コンソール画面には何行ものnullエラーが並びます(ツライ)。

その為、エディタ上で編集した値を保存したい場合には、MonobehaviourもしくはScriptableObject継承のクラスを用いて、Hierarchyに配置、もしくはScriptableObjectのアセットを作成するなどして値を保存するようにしましょう。

【Unity5】名前指定でAddComponent

過去『AddComponent("クラス名")』でコンポーネントを追加することができましたが、既にそれは『[使用しないでください]』と注意されるように、使用することが禁じられています。

 

じゃあ名前文字列で指定して追加するにはどうすればよいかというと、『Type.GetType(string typeName)』関数を使用します。

これは、文字列指定したクラスをType型にしてくれる関数です。

AddComponentを文字列で使用することは出来なくなりましたが、Type型で指定することは可能です。

具体的には、

using System;

(略)

this.AddComponent(Type.GetType("追加したいクラス名")) ;

で可能。

注意するべき点として、「namespace」を使用している場合は、キチンと「(namespace名).(追加したクラス名)」と書かないとnullを吐きます。

 

私だけかもしれませんが、ここ地味に躓きました。

しかもよく見れば引数の一覧?にちゃんと「Type型使えるよー」って教えてくれてるんですよね。なぜ気づけなかったのか。