UnityはCubeとかSphereなどのプリミティブが最初から用意されてて、さくっとなんか試したいときには便利ですよね。しかし3D用のプリミティブはあれど2D用のプリミティブは無い…?Unity2Dでも簡単に使えるプリミティブが欲しい。3Dオブジェクトを置いて2Dのコライダーを入れれば代用できなくもないけどスプライトで欲しいなあ。なんて思って、完全に自分用にSpritePrimitiveなるパッケージを作りました。そのときの備忘録です。
- Sprite Primitiveのイメージ
図のようにCreate -> 2D Object -> Primitives で形状を選択すれば目当てのスプライトプリミティブを生成できます。超簡単。ためしに Circle を選ぶと…
Sceneビューの中心に円形のスプライトを持ったゲームオブジェクトが生成されます。Circle Collider 2Dもくっついてます。
ダウンロード(SpritePrimitive.unitypackageをGoogleドライブ上で公開)
- Sprite Primitiveの構成
構成は次の図の通り。
パッケージ中のソースコードはSpritePrimitiveEditor.csのひとつだけ。Spritesフォルダにはプリミティブの見た目の形状となるスプライトを。Resourcesフォルダ以下のプレハブはコライダーもくっつけてそのまま使えるようにしたもの。Createメニューからプリミティブを選択すると、Resourcesフォルダ以下の対応するプレハブを元にオブジェクトが生成されているというわけです。
- Sprite Primitiveのソースコード
// SpritePrimitiveEditor.cs #if UNITY_EDITOR using UnityEngine; using UnityEditor; using System.Collections; namespace SpritePrimitive { public class SpritePrimitiveEditor : MonoBehaviour { // アセットからゲームオブジェクトを生成 private static void InstantiateGameObject(string path) { Camera sceneCamera = SceneView.GetAllSceneCameras()[0]; // Sceneビューのカメラを取得 Vector3 centerPos = sceneCamera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0f)); // Sceneビューの中心位置をワールド座標で取得 Object obj = Resources.Load("SpritePrimitives/" + path); // Resourcesフォルダからアセットを取得 GameObject go = Instantiate(obj) as GameObject; // GameObjectとして生成 Selection.activeGameObject = go; // 生成したゲームオブジェクトを選択している状態にする go.name = obj.name; // GameObject名を設定 go.transform.position = (Vector2)centerPos; // Sceneビューの中心に配置 go.transform.rotation = Quaternion.identity; // 初期姿勢の設定 } // Createメニューの拡張 [MenuItem("GameObject/2D Object/Primitives/Box")] private static void Box() { InstantiateGameObject("Box"); } [MenuItem("GameObject/2D Object/Primitives/Circle")] private static void Circle() { InstantiateGameObject("Circle"); } [MenuItem("GameObject/2D Object/Primitives/Triangle")] private static void Triangle() { InstantiateGameObject("Triangle"); } [MenuItem("GameObject/2D Object/Primitives/Pentagon")] private static void Pentagon() { InstantiateGameObject("Pentagon"); } [MenuItem("GameObject/2D Object/Primitives/Hexagon")] private static void Hexagon() { InstantiateGameObject("Hexagon"); } [MenuItem("GameObject/2D Object/Primitives/Star")] private static void Star() { InstantiateGameObject("Star"); } } } #endif
自分でも驚くくらい丁寧にコメントが書かれています。過去の自分えらい。
「Createメニューの拡張」以下の部分がそのまんまエディタ拡張部分の記述です。
[MenuItem(“path”)]と書くと、そのpath位置に項目を追加でき、それをクリックしたときに[MenuItem(“path”)]以下の静的メソッドが呼び出されます。この記事タイトルの「メニューの追加」がこの部分のことです。
そして上半分のInstantiateGameObject(string path)がゲームオブジェクトの生成を行っている部分です。ここでやっていることはソースコードのコメントの通り。
特筆しておくことといえば、UnityにおけるResourcesフォルダの振る舞いのことくらい。実はResourcesフォルダはランタイムでアセットを読み込む際などに使うフォルダです。Resourcesフォルダ以下のファイルにはスクリプトからResourcesクラスを介してアクセスすることができるのです。このソースコードでいうと17行目がそれですね。
Resourcesクラスについての詳細は公式のリファレンスでどうぞ。
要するに、エディタに新しいメニュー項目を作って、それをクリックしたらResourcesフォルダにあるアセットをコピーしてオブジェクトを生成。このスクリプトでやってることはこれだけです。
- スプライトの設定
最後に一つだけ。
プリミティブの基本スケールはちゃんとUnityの1ユニットに合わせたいですよね。例えばScale 1のBoxプリミティブを生成し、Box左下隅をWorld座標(0, 0)に合わせたとき、Box右上隅の点はWorld座標で(1, 1)であってほしいわけです。
そこで重要なのが、スプライトのインポート設定の「Pixels Per Unit」という項目。この項目の意味はその名の通りで、Unityの1ユニットに元画像の何ピクセルをあてるか、というもの。今回はBox用スプライトとして、128×128ピクセルすべて真っ白の画像を用いています。そしてこの一辺を1ユニット相当としたいのでPixels Per Unitを128と設定しています。
思ったより長くなってしまった。こんなはずでは。
おわりです。デレステやります。