おもちゃラボ

Unityで遊びを作ってます

【Blender】イラレで描いた図形を立体化する

ここではイラストレータで作成した図形をBlenderで立体化する方法を紹介します。
思っていたよりも手間がかかります・・・。

f:id:nn_hokuson:20190610202448p:plain:w600

イラストレータでの作業

今回はイラストレータで作った、次のような図形を立体化することにします。このイラストは星型の図形ツールで書いただけのものです。

f:id:nn_hokuson:20190610200821p:plain:w250

まずはイラストレータで作成したパスをアウトライン化します。アウトライン化することで「太さを持った線」が「パスで囲まれた領域」に変換されます。

アウトライン化するにはメニューバーの「オブジェクト」から「パス/パスのアウトライン化」を選択します。先の内側と外側にもパスが設定されているのが分かりますね。

f:id:nn_hokuson:20190610201015p:plain:w250

アウトライン化できたらSVG形式で保存します。「名前をつけて保存」でフォーマットにSVGを指定して保存してください。

f:id:nn_hokuson:20190610201147p:plain

写真をアウトライン化したい場合

イラレで線画を描くのではなく、写真やすでにあるイラストをパスに変換して使いたい場合には、次の記事が参考になりました。
liginc.co.jp

Blenderでの作業

ここではBlender2.8を使って説明しますが、基本的にBlender2.7以前でも同じだと思います。

保存したSVGファイルをBlenderに読み込みます。メニューバーの「ファイル」から「インポート/scalable vector graphic(.svg)」をクリックして、ファイルを選択します。

f:id:nn_hokuson:20190610201911p:plain:w300

読み込んだ図形はパスのままで扱いにくいため、メッシュに変換します。オブジェクトモードで図形を選択した状態で、「オブジェクト」→「変換」→「カーブ/メタ/サーフェイス/テキストからメッシュ(Alt+C)」を選択してパスをメッシュ化します。

f:id:nn_hokuson:20190610202207g:plain:w500

これでイラストレータで作成した図形が、通常のメッシュとして扱えるようになりました。あとは頂点の押し出し(E)などを使って立体にすれば完成です。

f:id:nn_hokuson:20190610202448p:plain:w500

【Unity】ARKitで水平面か垂直面かを調べる

 ARKitで検出した平面が水平面なのか垂直面なのかを調べて、床面と壁面でことなる平面オブジェクトを作りたいことがあります。

 検出された平面のRotationを確認しても良いのですが、ARKitでは水平面か垂直面かを調べる方法が提供されているので、それを使う方法を紹介します。

f:id:nn_hokuson:20190424194246j:plain

なぜか、写真に収まろうとするコジロー・・・

Unityで平面の方向を調べる

 検出した平面が床面か壁面かは、ARPlaneAnchorクラスのalignmentプロパティで調べることができます。このプロパティはARPlaneAnchorAlignment型で、次のふたつが定義されています

プロパティ 意味
ARPlaneAnchorAlignmentHorizontal 水平面
ARPlaneAnchorAlignmentVertical 垂直面

 

 Unity ARKit Pluginでは平面が検出されると、UnityARUtilityクラスの中のCreatePlaneInSceneメソッドが呼び出されます。このメソッドの引数に渡されるARPlaneAnchorの情報をもとにして平面を作成します。そこで、このCreatePlaneInSceneメソッドの中で、水平面か垂直面かを調べて、作成する平面の色を変えてみましょう。

f:id:nn_hokuson:20190424200152p:plain:w450

水平面か垂直面かを調べる

 UnityARUtilityクラスのCreatePlaneInSceneメソッドを次のように書き換えてください。

public static GameObject CreatePlaneInScene(ARPlaneAnchor arPlaneAnchor)
{    
    GameObject plane;
    if (planePrefab != null) {
        plane = GameObject.Instantiate(planePrefab);
    } else {
        plane = new GameObject (); 
    }

    plane.name = arPlaneAnchor.identifier;

    ARKitPlaneMeshRender apmr = plane.GetComponent<ARKitPlaneMeshRender> ();
    if (apmr != null) {
        apmr.InitiliazeMesh (arPlaneAnchor);
    }

    // 平面の向きによって平面オブジェクトの色を変える
    if(arPlaneAnchor.alignment == ARPlaneAnchorAlignment.ARPlaneAnchorAlignmentVertical)
    {
        plane.transform.Find("Plane").GetComponent<MeshRenderer>().material.color = new Color(1,1,0);
    }
    return UpdatePlaneWithAnchorTransform(plane, arPlaneAnchor);
}

 
 ここではreturnの直前の行で、引数で受け取ったarPlaneAnchorのalignmentプロパティを調べています。alignmentが水平面(ARPlaneAnchorAlignmentHorizontal)なら青色、垂直面(ARPlaneAnchorAlignmentVertical)なら緑色に塗り分けています。

 また、マテリアルの色を指定するため、UnityARKitPlugin/Examples/Common/Prefabs/にあるdebugPlanePrefabのシェーダは「Sprites/Default」に変更しています。

f:id:nn_hokuson:20190424194835p:plain:w450

壁と床で表示するオブジェクトを変えたい場合は・・・

 もし、壁面と床面で生成するオブジェクト自体を変えたい場合は、次のような手順で壁用のPrefabを作成&追加し、CreatePlaneInSceneの中でalignmentを見てPrefabを生成し分ければ、床と壁で違うオブジェクトが表示されます。

  1. UnityARGeneratePlaneに壁用のPrefabを追加
  2. UnityARUtilityにも壁用のPrefabを追加
  3. UnityARUtilityのInitializePlanePrefabで壁用のPrefabも渡せるようにする
  4. インスペクタから壁用のPrefabをセットする

実行してみた

 ちゃんと床と壁で違う色の平面になっていることが確認できました。何回とっても邪魔するよね・・・(笑)
f:id:nn_hokuson:20190424195026j:plain:w450

booth.pm

【Unity】インべーダ作りで学ぶUnet超入門3

前回はUnetを使って、ネットワークで同期して動くプレイヤ(自機)を作成しました。今回はネットワークで同期して動くインベーダーを作りましょう。

【Unity】インベーダ作りで学ぶUnet超入門1 - おもちゃラボ
【Unity】インベーダ作りで学ぶUnet超入門2 - おもちゃラボ

インベーダを作る

Unetを使って敵キャラなどプレイヤ操作の必要がないオブジェクトを作る場合、基本的にはサーバー「だけ」で生成し、サーバー「だけ」で動かすようにします。

 このように、サーバーにすべておまかせすることで、クライアントの状態によらずに、どのクライアントでも同じ動きを保証できます。逆にクライアントで敵の座標などをいじってしまうと、クライアント間で同期がとれずに齟齬が生じてしまうので注意が必要です。

f:id:nn_hokuson:20190306211450p:plain:w400

では実際に敵(インベーダー)を作っていきましょう。インベーダーを作る流れは、まずインベーダーのPrefabを作り、次にそれを生成するマネージャクラスを作ります。この設計の流れはUnityの教科書と同じなので、そちらを参考にしてください。

まずはインベーダーのPrefabをつくるため、プロジェクトウィンドウからenemyをシーンにドラッグ&ドロップしてください。

f:id:nn_hokuson:20190306212217j:plain

ネットワークで位置を同期するために、インスペクタのAdd ComponentボタンをクリックしてNetwork→Network Transformコンポーネントをアタッチしてください。

f:id:nn_hokuson:20190306212413j:plain

次にインベーダーを左右に移動させるスクリプトを作成します。名前をEnemyControllerに変更して次のスクリプトを入力して下さい。

using UnityEngine;
using UnityEngine.Networking;

public class EnemyController : NetworkBehaviour {

    int moveDir = 1;
    int moveCount = 0;

    void Update ()
    {
        if (!isServer) return;
        if (Time.frameCount % 50 != 0) return;

        moveCount++;

        if (moveCount == 5)
        {
            moveCount = 0;
            moveDir = -moveDir;
        }
		transform.Translate(moveDir, 0, 0);
    }
}

ここでもMonoBehaviorの代わりにNetworkBehaviorを使用していることに注意してください。

Updateメソッドの中で左右への移動処理を書いています。大切なのは、Updateの先頭でisServerがtrueでなければreturnしている部分です。このように書くことで、以下の処理はサーバーのみで行われるようになります。

スクリプトを記述できたらInvaderオブジェクトにドラッグ&ドロップしてアタッチしておきましょう。

f:id:nn_hokuson:20190306212549j:plain

最後にヒエラルキーウインドウからプロジェクトウィンドウにInvaderをドラッグしてPrefabを作成します。Prefabの名前はenemyPrefabに変更してください。

f:id:nn_hokuson:20190306212725j:plain:w500

ヒエラルキーウインドウのenemyPrefabは不要なので削除しておいてください。

f:id:nn_hokuson:20190306212923j:plain:w250

インベーダを生成するクラスを作る

次にいま作成したインベーダーのPrefabからインベーダーを複数作るジェネレータクラスを作りましょう。ジェネレータも、インベーダーと同じくサーバーのみで動作するようにします。

まずはヒエラルキーウインドウでCreate→Create Emptyを選択して空のオブジェクトを作成し、名前をEnemyGeneratorに変更しておきます。

f:id:nn_hokuson:20190314091938p:plain:w300

次にジェネレータ用のスクリプトを作ります。先程と同様にプロジェクトウィンドウでC# Scriptを作り、名前をEnemyGeneratorに変更しましょう。変更できたら次のスクリプトを入力して下さい。

using UnityEngine;
using UnityEngine.Networking;

public class EnemyGenerator : NetworkBehaviour
{
    public GameObject enemyPrefab;

    void Start()
    {
        if (!isServer) return;

        for (int y = 0; y < 3; y++)
        {
            for (int x = 0; x < 5; x++)
            {
                GameObject go = Instantiate(enemyPrefab,
                                            new Vector3(-5f + x * 2, y*1.5f+1.0f, 0),
                                            Quaternion.identity);
                NetworkServer.Spawn(go);
            }
        }
    }
}

このスクリプトでもStartメソッドの最初でisServerかをチェックしています。そしてサーバーであればインベーダーを5体を3段ぶん生成しています。このように、サーバーだけで動作させたいスクリプトは必ずisServerでチェックするようにしましょう。

インベーダーを生成するには一旦おなじみのInstantiateメソッドを使ってインベーダーオブジェクトを作っています。ただ、Instantiateするだけだと、サーバーでしかインベーダーは生成されません。各クライアントにも同じオブジェクトを生成するにはNetworkServer.Spawnメソッドを使ってSpawnする必要があります。Spawnの引数にはクライアントでも生成したいゲームオブジェクトを渡します。

 スクリプトができたら、ヒエラルキーウインドウのEnemyGeneratorにドラッグしてアタッチします。Enemy Prefabの欄には、プロジェクトウィンドウの「enemyPrefab」をドラッグ&ドロップします。

f:id:nn_hokuson:20190314092732j:plain

ネットワークマネージャーにPrefabを登録する

これで完成!といいたいところですが、ネットワーク上で動的に生成するPrefabはすべてNetworkManagerに登録する必要があります。これ、超忘れがちなので注意してください。(忘れても最近はちゃんとエラーメッセージを出してくれるようです)

ヒエラルキーウインドウでNetworkManagerを選択してRegistered Spawnable Prefabsの欄の「+」をクリックします。欄が新たに作成されるので、そこにプロジェクトウィンドウからenemyPrefabをドラッグ&ドロップしてください。

f:id:nn_hokuson:20190314093056j:plain

これで実行すれば、次のように敵を生成できます。ビルドして動作確認しておきましょう。

f:id:nn_hokuson:20190314093943g:plain:w500

次回は自機から弾を発射してインベーダーをやっつけられるようにします。