Unity3Dタイルマップ作成時の注意点

Unity3D has a tile map system that allows you to make 2D maps with no programming at all. This tool is available for free with the unity base package and can be expanded with the 2D extras package found in unity’s GitHub. It’s fairly easy to use but if you are not careful when setting it up you could have a hard time.  

Unity3Dのタイルマップはプログラミングなしで簡単2Dのマップを作れるツールです。このツールはUnity3Dのベースパッケージに入ってて、UnityのGitHubにある2D-extrasのパッケージで機能拡張できます。使いやすいものですがセットアップで間違えると痛い目に会います。 

On this post I will be going through a couple details that you should take in account when making your tile map. 

このポストでは、タイルマップを作っている時の4つの注意点をあげます 

1.スプライトシート 

Tiles usually come in sprite sheets. A sprite sheet is an image containing a series of smaller images most commonly of the same size and organized in columns and rows. 

普段はタイルがスプライトシートにあります。スプライトシートというのは小さくて同じサイズの複数のイメージが含められているのイメージをさします。 

After importing a sprite sheet into your project. Unity allows you to divide the sheet into individual tiles. To do this you click on the sprite sheet and look at the inspector, the texture type should be “Sprite (2D and UI)” and the sprite mode set to “Multiple”. To start the division process click on the Sprite Editor button. During this process there are a couple factors you should take in account: 

Unityにインポートをして選択されたままにInspectorで複数のイメージにスライスが可能です。このプロセスに注意点が二つあります。 

  • Pixels per unit :  Before even going into the Sprite Editor make sure that the value of the “Pixels Per Unit” parameter on the inspector is the same size of your tiles. E.g: if your sheet is holding 64×64 px tiles then make the “Pixels per unit” 64. If you forget this step your tiles will have padding or overlap each other. 
  • Pixels Per Unit: 「Sprite Editor」に入る前に「Pixels Per Unit」のパラメータがタイルのピクセルサイズと同じにしないといけません。例えばスプライトシートに64px✖64pxのタイルが入ってたら、「Pixels Per Unit」を64に設定してください。設定が違うとパディングがはいったり、タイルが重なったりします。
  • Pivot: When using the slice tool in the Sprite Editor. Make sure to set the pivot to “center”. If you miss this step the tiles will be offset from the grid in the scene.
  • Pivot: スライスツールを作っているときに、「Pivot」のパラメータが「Center」を選択しないと、タイルがシーンのグリッドからオフセットされます。 

2.コライダー 

Sprites are just the visual part of our tiles. Without colliders, even if we can see the ground, our characters would just fall off the world. Unity made it very easy to add colliders to your grid. Just select the tilemap and add a “Tilemap Collider 2D” component to it. This is fast and easy but it creates a collider for every tile with a sprite on it. This means that even tiles surrounded by other tiles that will never be in contact with anything will also have a collider. I.e: A lot of unnecessary colliders that will never be used and use resources are being created. 

スプライトはタイルの見た目だけのものなのでコライダーを付けないと、床を見えても、キャラクターが突き抜けて落ちます。コライダーを付けるのは簡単にできます。Tilemapのオブジェクトに「Tilemap Collider 2D」というコンポーネントを付けることで付けますが、これで全てのタイルにコライダーが適用されてしまいます。言い換えるならば一回も使わない無駄なコライダーが生成されてリソースを余分に使っています。 

 

To prevent this, we can add an other component. The “Composite Collider 2D” component. This component requires a little bit of setup. When this component is added to your tilemap GameObject, it automatically adds a RigidBody2D component to the object. If you are adding collision to static objects like the ground or a wall, make sure to set the “Body Type” of the RigidBody2D component to “Static” or it will fall down with gravity. Now go back to your “Tilemap Collider 2D” component and tick on the “Used By Composite” box. This will merge you colliders leaving only the outer colliders. 

これを防ぐためもう一個のコンポネントを付けられます。「Composite Collider 2D」というコンポネントです。追加するときに自動でRigidBody2Dが追加されます。もし動かない物のコライダーを作っているときに(床、壁、など)RigidBody2Dの「Body Type」を「Static」にしないと重力で落ちます。最後に「Tilemap Collider 2D」の「Used By Composite」を選択することで内側の無駄なコライダーがなくなります。

 

3.Rule Tiles: 

Rule tiles are part of the “2D extras” package you can find in Unity’s GitHub (https://github.com/Unity-Technologies/2d-extras).  

UnityのGitHubの”2D-extras”のパッケージにRule Tilesという物があります。(https://github.com/Unity-Technologies/2d-extras).  

This tiles can save you a lot of time when map designing, by allowing you to create a set of rules for which sprite is displayed depending on the tiles around it.  

このタイルのルールを設定するとマップをつくる際にタイルのスプライトを周りにあるタイルを見て設定されるので、これレベルデザインの時間を節約できます。 

 

Rule tiles also make it easy to create tile animations. 

そして、タイルアニメーションもつくれます。 

 

One thing to take care of when setting up this rules is the order you add them to the list. When unity is checking your list of rules it stops and selects the first sprite that fulfills the rule. So if you have a more specific set of rules after a very general one, it might be ignored. 

ルールを設定するときの注意点はルールの順番です。Unityがチェックするたびに先にルールに一致するスプライトが選択され、残っているルールを無視します。つまり、幅広いルールを持っているスプライトの次に細かいルールを適用するものがあった場合、幅広いルールに合致して、細かいルールが無視される可能性が高いです。 

 

Getting the tiles to work as you intend them to, might take some time but once you have it set up, it will speed up your map making time. And if you decide to change a sprite midway through the map design, you can just change the sprite for that rule without having to touch the tiles in the scene. A good rule of thumb is to keep the tiles with very specific rules at the top of the list and the more general ones at the bottom. And remember you can drag the rules up and down within the list to change the order easily. 

思った通りに設定するには時間がかかるかもしれませんが、一度設定したら、マップ作りがスピードアップします。そして途中でタイルのスプライトを変更したい場合もシーンにあるタイルを作り直しは必要ないです。そのルールのスプライトを変更することで済みます。経験を積んだら設定時間も減ります。ヒントとしては細かいルールを幅広いルールより先に付けること。リスト内での順番がドラッグで簡単に変更できます。 

4.レイヤー: 

Layers are powerful tools that help us keep rendering orders separate, make pretty overlaps and set colliders to some objects while ignoring others. But it’s important you keep track of the layer you are working on at all times, because just as any other software that uses layers like Photoshop or Gimp, editing the wrong layer will cause you problems. So if you run into some weird behavior like colliding into clouds, falling through the ground or tiles not animating correctly, make sure you check everything is on the right layer. To help you with this Unity has an option to gray out all layers except the one you are working on. This option can be turned on by changing the “Focus On” parameter located on the Scene window to “Tilemap.  

レイヤーは非常に便利なツールでレンダリング順を設定するのに役立ちますが、PhotoshopやGimpのように間違えたレイヤーに間違えたタイルを入れるミスは起こりやすいので気を付けないといけません。雲にぶつかったり、床を突き抜けたり、タイルアニメーションが変になったりしていた場合は、レイヤーに正しくアサインされているかの確認をおすすめします。レイヤーに関するトラブルを防ぐためにUnityには変更中のレイヤー以外のレイヤーをグレーアウトする機能を持っています。シーンにあるの「Focus On」パラメータを「Tilemap」に変更することで起動されます。 

(Unity3D)ダイナミックボタンアクション

あるゲームのUIを作っている間にボタンに関する問題に遭遇しました。選択されたエンティティによって、表示されているボタンが変わるようにしたいと思いました。

もちろん一つずつのエンティティのGameObjectを準備して場合によってSetActive()trueかfalseにしてもいいですが、スケーラビリティは?エンティティを追加しようとする毎に新しいGameObjectを準備しますか?そんな面倒なことは勘弁してほしいですよね。

ということでスケーラビリティのため、エンティティを選択する時にボタンが編集され、ちょっとしたアルゴリズムを書きました。 

こういうシステムが入っているUIを作る人がいて参考になればと思い、分かりやすいバージョンを準備して下記にそのケースを紹介します。 

想定ケース: 

1.エンティティによる、ボタンの数が異なる場合でもOK。 

2.ボタンの見た目を編集可能にする。 

3.ボタンのアクションがstringの引数を受け取れる。 

4.簡単にするため、ボタンの最大数は10にします。 

先ずはボタンの情報をまとめるクラスActivityButtonをつくります 

public class ActionButton {
    public UnityAction<string> theAction;
    public string argument;
    public Sprite sprite;
    public ActionButton(UnityAction<string> act, Sprite sprite, string arg=""){
        theAction = act;
        argument=arg;
        this.sprite = sprite;
    }
}

UIマネージャーにはButton、Image、GameObjectの配列を必要です。パブリックなGameObjectの配列を作ってシーンからボタンをドラッグして、Start()でButtonとImageの配列を作りました 

public class MyEpicUIManager : MonoBehaviour {
    Button[] actionBtns;
    Image[] actionImgs;
    public GameObject[] actionBtnObjs;

    void Start () { 
        // Action Panel Setup
        actionBtns = new Button[actionBtnObjs.Length];
        actionImgs = new Image[actionBtnObjs.Length];
        for(int i=0; i<actionBtnObjs.Length; i++){
            actionBtns[i]=actionBtnObjs[i].GetComponent<Button>();
            actionImgs[i]=actionBtnObjs[i].GetComponent<Image>();
        } 
    }
}

ボタンをゲットしたので、次はActionButtonのリストを取得しボタンを編集するメソッドを作りました。そしてボタンをクリアするメソッドも必要です。 

public void SetActions(List<ActionButton> btnActions){
    ClearActions();
    for(int i=0; i<btnActions.Count; i++){
        actionBtnObjs[i].SetActive(true); 
        UnityAction<string> action = btnActions[i].theAction;
        string arg = btnActions[i].argument;
        actionBtns[i].onClick.AddListener(()=>action(arg)); 
        actionImgs[i].sprite = btnActions[i].sprite; 
    } 
}

public void ClearActions(){
    for(int i = 0; i<actionBtnObjs.Length; i++){
        actionBtns[i].onClick.RemoveAllListeners();
        actionBtnObjs[i].SetActive(false);
    }
}

これでUIマネージャーが完了です。Start()でClearActionsを呼べば、ゲームを起動する際にボタンクリアできます 

エンティティにはそれそれに自分のActionButtonリストが必要です。UIマネージャーのSetActionsを呼べるようにする必要があります。そしてアクション自体も必要です。

public class MyEntity1 : MonoBehaviour {
 
    public MyEpicUIManager ui;
    List<ActionButton> uiActions = new List<ActionButton>();

    void Start(){
        uiActions.Add(new ActionButton(Action1,Resources.Load<Sprite>("Sprites/E1A1"))); 
        uiActions.Add(new ActionButton(Action2,Resources.Load<Sprite>("Sprites/E1A2"), "Test argument 1")); 
        uiActions.Add(new ActionButton(Action2,Resources.Load<Sprite>("Sprites/E1A2"), "Test argument 2")); 
        uiActions.Add(new ActionButton(Action3,Resources.Load<Sprite>("Sprites/E1A3"), "3"));
        uiActions.Add(new ActionButton(Action3,Resources.Load<Sprite>("Sprites/E1A3"), "5")); 
    }

    // An action that doesn't really use the string provided 
    public void Action1(string s =""){
        print("Entity 1 Action 1");
    }

    // An action that uses the string
    public void Action2(string s =""){
        print("Entity 1 Action 2 Argument: " + s);
    }

    // An action that changes the string to an int
    public void Action3(string s =""){
        string msg = "Entity 1 Action 3 ->"+s+" hashes: ";
        int n = 0;
        int.TryParse(s, out n);
        for(int i=0; i <n; i++) msg+="#";
        print(msg);
    }

    public void OnSelect(){
        ui.SetActions(uiActions);
    }
} 
public class MyEntity2 : MonoBehaviour {
 
    public MyEpicUIManager ui;
    List<ActionButton> uiActions = new List<ActionButton>();

    void Start(){
        uiActions.Add(new ActionButton(Action1,Resources.Load<Sprite>("Sprites/E2A1"))); 
        uiActions.Add(new ActionButton(Action2,Resources.Load<Sprite>("Sprites/E2A2"), "Test argument 1")); 
        uiActions.Add(new ActionButton(Action2,Resources.Load<Sprite>("Sprites/E2A2"), "Test argument 2")); 
        uiActions.Add(new ActionButton(Action2,Resources.Load<Sprite>("Sprites/E2A2"), "Test argument 3"));
        uiActions.Add(new ActionButton(Action3,Resources.Load<Sprite>("Sprites/E2A3"), "3"));
        uiActions.Add(new ActionButton(Action3,Resources.Load<Sprite>("Sprites/E2A3"), "5")); 
        uiActions.Add(new ActionButton(Action3,Resources.Load<Sprite>("Sprites/E2A3"), "7")); 
    }

    // An action that doesn't really use the string provided 
    public void Action1(string s =""){
        print("Entity 2 Action 1");
    }

    // An action that uses the string
    public void Action2(string s =""){
        print("Entity 2 Action 2 Argument: " + s);
    }

    // An action that changes the string to an int
    public void Action3(string s =""){
        string msg = "Entity 2 Action 3 ->"+s+" asteriscs: ";
        int n = 0;
        int.TryParse(s, out n);
        for(int i=0; i <n; i++) msg+="*";
        print(msg);
    }

    public void OnSelect(){
        ui.SetActions(uiActions);
    }
}

こちらはエンティティのサンプルです。OnSelectはキーボードかシーン上のボタンで試してみてください。このサンプルを動かすため “Assets/Resources/Sprites”に、正しい名前のスプライトを入れてください。そしてスプライトのImport SettingsのTexture Typeは “sprite (2D and UI)”にする必要があります。 

シーンに必要なものはCanvasと7つ以上のボタンです。UIマネージャーをシーンに追加して、インスペクターでボタンをドラッグします。エンティティもシーンに追加してインスペクターでUIマネージャーをドラッグしてください。 

それで完了です。短くて簡単な例となりますが、これでエンティティを追加ときにいちいち新しいパネルを準備する必要がなくなります。そしてアクションを追加したり削除する場合でも、リストから追加したり削除することで実現できます。

特定のUI要素上でマウスのローカルポジションを見つける

まず、特定のUI要素が特定されているかどうかを調べる必要があります。UI要素がマウスでクリックされているかどうかを判断する方法は複数ありますが、最も一般的な方法はUnityのPointerEventSystemを使用することです。使用できるようにするには、イベントをトリガーするために必要なさまざまなインターフェースと“using UnityEngine.EventSystems;”というNamespaceを実装する必要があり、SceneのHierarchyにEventSystemを追加することも必要です。また、スクリプトはトリガーされたオブジェクト上にある必要があります。
 
このEventSystemの詳細については、このリンクをご覧ください:
https://docs.unity3d.com/ScriptReference/EventSystems.PointerEventData.html

EventSystemの実装例を次に示します:

別の方法はUnityのBoundsを使用します。

イメージのようなUIオブジェクトでそれを使用できるようにするには、コライダーを置いてオブジェクトと同じサイズにする必要があります。次に、Boundsを見つかるために、このコライダーコンポーネントをスクリプトで取得します。これにより、マウスの位置がこれらの範囲内にあるかどうかを確認することができます。

Bounds についての詳細は、このリンクをチェックしてください:

https://docs.unity3d.com/ScriptReference/Bounds.html

Bounds の実装例を次に示します:

オブジェクトをHoveringしていることがわかったので、次のステップは、マウスの位置を世界に相対的ではなく、そのオブジェクトに関連させて見つけることです。そのために、UnityのRectTransformUtilityを使用することができます。ローカルのRectTransformを新しいスクリーン位置として設定することができます。

RectTransformUtility についての詳細は、このリンクをしてください:

https://docs.unity3d.com/ScriptReference/RectTransformUtility.html

https://docs.unity3d.com/ScriptReference/RectTransformUtility.ScreenPointToLocalPointInRectangle.html

RectTransformUtility の二つの実装例を次に示します、1つはBoundsシステムを使用し、もう1つはEventSystemを使用します。

Boundsシステム:

EventSystem:

*canvasがScreen space – Overlayに設定されているため、カメラパラメータはnullに設定されています。
このlocal mouse positionはいろいろな使い方ができます。 たとえば、カンバスにある、レンダーテクスチャを使用してミニーマップ上でクリック位置を取得することができます。

Vuforiaで大きいオブジェクトを表示するためのアドバイス

Vuforiaで大きいオブジェクトを表示するためのアドバイス

 

  • マーカーとそのサイズとの関係

VuforiaのImageTargetと表示されて欲しいオブジェクトのサイズは相関関係があります。
例えばオブジェクトを2m*2mのサイズで表示したい場合、ImageTargetのサイズを0.2m*0.2mにして、
印刷したイメージも0.2m*0.2mサイズに出力をしなければなりません。
もし正しく設定されていなければ、オブジェクトが表示されるときの比率が壊れてしまいます。

 

  • オブジェクトをその場に残す方法

大きいオブジェクトの全体を表示するためにVuforiaのExtended Trackingはとても便利です。
ImageTargetのオプションをEnableにすると、
イメージからスマートフォンのカメラがちょっと離れてTrackingがなくなっても、
オブジェクトが残ります。

ARCameraのゲームオブジェクトのWorld Center ModeオプションをCameraにすると、
オブジェクトを中心に回り込むような表示がかのうです。

イメージターゲットは床でも壁でも、Unity内でオブジェクトのローテーションを変えることで、対応可能です。

 

  • マーカーとカメラとの角度で発生する問題

オブジェクトのカメラを通しての見え方(カメラアングル)はマーカーを 認識したときにデフォルトの仰角が決まります。

 

  • 床においたマーカーとスマートフォンのカメラとの角度に関する注意点

そのため、床にマーカーを置きオブジェクトを表示した場合、
カメラのポジション(デフォルトの仰角)はマーカーを認識した際決定されるので、カメラを水平にして、
オブジェクトが正面ではなく少しずれた表示になります。

 

  • 床にマーカーを配置した際のスマートフォンのカメラとの距離に関する注意点

マーカーを床に置くとカメラとイメージが必然的に遠く離れるため(ユーザが立っている場合)
マーカーとユーザーのスマートフォンのカメラとの角度が浅くなるので認識しずらくなります。

 

  • 大きいオブジェクトのマーカーは壁に配置がおすすめ

壁にマーカーを配置する場合はその問題はないでしょう。
イメージを壁に置いたらその高さを測定し、
Unity内でのImageTargetの高さと現実の世界のイメージの高さを合わせてください。

 

  • イメージか、QRコードか

最近QRコードはとても人気になりました。雑誌とかデパートの製品とかにどこでも使みかけます。
VuforiaもQRコードが使えますが色々悪いポイントがあります、
大きな欠点はトラッキング(カメラからの認識)の問題です。

大きいオブジェクトを表示するには比較的大きなQRコードコードを全部をスキャンする必要があります。
カメラがQRコードを認識する際、スキャンの角度やライトの明るくさは足りないとTracking機能が正しく動作しないことが多くなります。
一方、イメージであればQRコードよりも認識の向上が期待できます。
イメージであれば、画像の一部を認識できれば、マーカーとして機能するからです。
(※QRコードは全体が認識されないとオブジェクトが表示されない)

 

  • 認識率を上げるため適切な写真を選ばなければなりません。
    Vuforiaが推奨するイメージの条件は下記の三つです。1)ディテールが多いイメージ。
    →頂点が画像の中に多く含まれるイメージ

     

    2)いいコントラストのイメージ。

    3)同じパターンではなく、色々な形が含まれている写真のほうがいい。
    →有機的なパターン
    https://library.vuforia.com/articles/Best_Practices/Attributes-of-an-Ideal-Image-Target

以上がカメラにやさしいイメージの条件ですが、一つ付け加えるならば方向が分かりやすいほうがよいでしょう。
3つの条件にあう写真に画像ソフトでマークを付けくわえれば、配置する際に人間にとって便利です。

HTC VIVEでポジションリセンターを行う(UNITY)

Oculus Riftに比べて、HTC Viveのドキュメントは決して豊富とはいえません。
UnityのUnityEngine.VR.InputTracking.Recenter(); の関数が使えないなど、UnityのネイティブVRサポートも正しく動作しないことがあります。
例えば、作成中のゲームでStanding ModeやSeated Mode使おうとした場合SteamVRのValve.VR.OpenVR.System.ResetSeatedZeroPose ();
を利用することになるとおもいますが、Room-Scaleでのプレイヤー位置のリセットの場合はその関数は動かないことを前提としえいる為利用することができません。
そこでルームスケールでの位置リセットを実現するため、スクリプトを作成しましたのでシェアしたいとおもいます。

[sourcecode lang="c-sharp"]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PositionRecenter : MonoBehaviour{

//必要な GameObjectをグラブします。
public GameObject avatar; //カメラリッグ
public GameObject eye; //カメラ
public GameObject[] hands; //両手

//ゲーム内のデフォルトポジションとスケールを設定します。
Vector3 defaultPos ;
public float defaultZ = 0f;
public float defaultY = 1f;
public float defaultX = 0f;

//ポジションOffsetを0に設定します。
float yDis=0f;
float xDis=0f;
float zDis=0f;

void Start (){

/*ゲームスタートのとき defaultPosを決めた
ゲーム内のスタート位置のデフォルト座標をX,Y,Zとして与えます。
パブリックフロートなのでインスペクターから変更可能です。
最後にSetPosの関数でゲームスタートのときポジションを
ゲーム内の初期ポジションにリセンターします。*/

defaultPos = new Vector3 (defaultX, defaultY, defaultZ);
SetPos ();
}

void Update (){

//In Gameでリセンターする場合矢印「↑」
if (Input.GetKeyDown (KeyCode.UpArrow)){
SetPos ();
}
}

public void SetPos(){

/*プレイヤーの頭のポジションと デフォルトポジション(ゲーム内での初期位置)のOffsetから計算しAvatarのポジションにoffsetの値を加えるとプレイヤーの位置とゲーム内での初期位置を合わせることができます。
Avatarのポジションはプレイヤーの現在の頭の位置の座標。*/

yDis = defaultPos.y - eye.transform.position.y;
xDis = defaultPos.x - eye.transform.position.x;
zDis = defaultPos.z - eye.transform.position.z;
avatar.transform.position = avatar.transform.position + new  Vector3 (xDis, yDis, zDis);

//手のポジションを、Avatarと一緒に移動します。
foreach (GameObject h in hands){
h.transform.position = h.transform.position + new Vector3  (xDis, yDis, zDis);
}
}
}
[/sourcecode]


上記のスクリプトでは、VRの体験者はPlayRoomのRoomScale内のどこでもいても、ゲームを起動するとスタートの際、ゲーム内の初期ポジションに配置されます。
そしてゲーム始まった後、またその決めた場所に戻させるスクリプトです。
ゲームが一旦始まった場合でも矢印↑キーをおせば、ゲームの初期ポジションに戻すことができます。
細かい部分についてはまた次回に説明したいと思います。

HoloLensの開発(Unity)

今回はUnityを使ってHoloLensでシンプルなCubeオブジェクトを表示するアプリをビルドしてみましょう。参考にするのは

https://docs.microsoft.com/ja-jp/windows/mixed-reality/develop/unity/tutorials

になります。
Microsoft
の上記のドキュメントをしっかり読んでもいいですが、私のようにイライラしてしまうといけないのでCubeを出すのに必要な部分を抜き出して手順を下記に説明してみます。

まず、HoloLensの値段を見て絶望しないでください、HoloLensなしでも開発できます。ただし、HoloLensがない場合はWindows “Pro”が必要です(できればWindows 10) Windows”Home” HoloLensのエミュレータが動きません。では、早速始めましょう。

  1. Unityで新しいプロジェクトを作ってください。
  2. キャメラのセットアップ。Main Cameraを選択してインスペクターで:
    1. Position(0,0,0)にしてください。
    2. Clear FlagsのドロップダウンをSkyboxからSolid Color
    3. BackgroundRGBA(0,0,0,0)にして「黒」
    4. Near Clip Plane0.85
  1. プロジェクト設定
    1. メニューバーのEdit > Project Settings > Qualityを選択します
      1. ウインドウズストアのロゴの下のドロップダウンからFastestを選択
    2. メニューバーのEdit > Project Settings > PlayerWindows StoreタブのOther Settings
      1. Virtual Reality Supportedをチェック
      2. Virtual Reality SDKsのリストにWindows Holographicが入っているを確認
  2. ホログラムを作り
    1. Cubeでもシーンに入れて
    2. Cubeを選択された状態でインスペクターでPosition(0,0,2)にしたらオブジェクトがユーザーのスタート位置から2m前にあるように見えます
    3. CubeScale(1,1,1)だったら1mCubeが見えますのでもうちょっと小さくしましょう(0.25,0.25,0.25)にしたら、25㎝のCubeになります。
    4. シーンを保存
  3. UnityからHoloLensでテスト(HoloLens必要)
    1. HoloLensのストアからHolographic Remoting Playerをインストールして、起動するとIP表示され、待機状態になります
    2. UnityのメニューバーでWindow > Holographic Emulation.を選択
    3. Emulation ModeNoneからRemote to Device
    4. Remote MachineでさっきのHoloLensIPアドレスを入力
    5. Connectをクリックして
    6. Connection Statusが緑のConnectedに変わったら、UnityPlayボタンでテストできます
  4. Visual Studioからのテスト(HoloLens有っても無くてもOKです!
    1. Unityのメニューバーから File > Build Settingsを選択
    2. Add Open Scenesにクリック
    3. PlatformのリストからWindows Storeを選択しSwitch Platformをクリック
    4. SDKUniversal10
    5. Target DeviceAny DeviceからHoloLens
    6. UWP Build TypeD3D
    7. UWP SDKLatest Installed
    8. Unity C# Projectsをチェック
    9. Buildをクリック
    10. エクスプローラーで新規フォルダを作って、選んで、Select Folderボタン押して
    11. Unityが終わったらフォルダの中には「プロジェクト名」.sln が作られたはずです
    12. 「プロジェクト名」.slnを開いてトップのツールバーのTargetDebugからReleaseにして、ARMからX86にしてください
    13. デプロイ:
      1. WIFIを使ってデプロイ:
        1. トップのツールバーのLocal MachineRemote Machineに変更
        2. HoloLensIPを入力
        3. Authentication ModeUniversal (Unencrypted Protocol)
        4. Debug > Start without debuggingをクリック
      2. USBを使ってデプロイ:
        1. トップのツールバーのLocal MachineDeviceに変更
        2. Debug > Start without debuggingをクリック
      3. エミュレータ使ってデプロイ:(HoloLens不要)
        1. Deviceの隣のやじるをクリックしてドロップダウンからHoloLens Emulatorを選んで
        2. Debug > Start without debuggingをクリック

これで初めてのHoloLensアプリができましたね

今回はただのCubeですが、今度はHoloLensの特徴を生かしてインタラクティブなアプリを作りましょう!

GLSLで書いたシェーダーをUnityのマテリアルにセット、WebGL出力してみます

Unityでシェーダーを書く機会は中々ありませんでしたが、今後のためにもGLSLでシェーダーを書いてみます。

GLSLの特徴はOpenGLに使えるという点です。ということは、スマートフォンゲームやWebGL(ブラウザ)に応用することもできます。また、言語的には、ほぼCに近い、ポインタのないC言語といった印象があります。(今回、細かな言語仕様の説明は割愛させていただきます。)

まずはGLSL on ブラウザで書く

まずは純粋にGLSLのフラグメントシェーダーだけを書いて試してみたいと思います。
これにはGLSL Sandboxというサイトを使います。このサイトでは、WebGL経由でシェーダーのGLSLコードをリアルタイムプレビュー、さらにjsdoitのようにギャラリーやフォークなどもできます。先にここで書く理由は、ちょっと寄り道してみたかったからです。w
WebでもUnityでもどっちでも動くと良いですし。

ちなみに、GLSL Sandboxの開発にはthree.jsの@mrdoob氏など、著名開発者が名を連ねています。

凝ると際限がないと思いますが、そこそこ見栄えしてきたら次のステップへいきます。

29g51155224222

デモはこちら : http://glslsandbox.com/e#29054.0

ソースは以下の通りです。
ポイントは(というほどではありませんが・・)時間経過を元にして三角関数を動かしているところです。

#ifdef GL_ES
precision mediump float;
#endif

uniform float time;
uniform vec2 resolution;

void main( void ) {

	vec2 position = ( gl_FragCoord.xy / resolution.xy ) - 0.5 ;
	vec3 col = vec3(0.0,0.0,0.0);
	
	//15個円を描く
	for(int i = 0; i < 15; i++){
		float red = 0.01  / abs(length(position +  float(i) * 0.1)  - 1.0 * abs(sin(time))) ;
		col.r += red;
	}
	
	//時間変化に合わせて色を変える
	col.g = abs(cos(time));
	col.b = abs(sin(time));

	gl_FragColor = vec4( col , 1.0 );

}

シェーダーのコードをUnityのマテリアルに持っていく

さて、フラグメントシェーダーのアルゴリズムができたので、Unityのマテリアルを作ってシェーダーをセットしていきます。

ここらへんは以下のページが非常に参考になりました。
「Unity で GLSL によるカスタムシェーダを利用する方法」
http://tips.hecomi.com/entry/20130324/1364136769

マテリアルを作って・・・

スクリーンショット 2015-11-25 18.21.19

シェーダーを作って・・・

スクリーンショット 2015-11-25 18.22.12

シェーダーを書いていきます。

GLSL SandboxのコードをUnity用にする。

先の参考ページをもとにシェーダーのコードを書いていきます。
というか、書いていて気づいたんですがUnityはシェーダーの変更があった場合、リアルタイムに反映してくれるんですね。こりゃ知らなかった。。良いですね。

出来上がったシェーダーはこちら

Shader "Custom/GLSL Shader" {
	SubShader {
		Pass {
			GLSLPROGRAM

#ifdef VERTEX
void main()
{
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif

#ifdef FRAGMENT
	uniform vec4 _Time;
	uniform vec4 _ScreenParams;

void main()
{
			
	vec2 position = ( gl_FragCoord.xy / _ScreenParams.xy ) - 0.5 ;
	vec3 col = vec3(0.0,0.0,0.0);
	float time = _Time.x * 15.0;

	for(int i = 0; i < 15; i++){
		float red = 0.01  / abs(length(position +  float(i) * 0.1)  - 1.0 * abs(sin(time))) ;
		col.r += red;
	}
	
	col.g = abs(cos(time));
	col.b = abs(sin(time));

	gl_FragColor = vec4( col , 1.0 );
}
#endif

			ENDGLSL
		}
	}
}

ここまでで、こんな感じです。(それっぽいcubeを足しました)

スクリーンショット 2015-11-25 18.32.26

いよいよ再生・・・

こうなりました・・・!!
先ほどのシェーダーが壁一面に広がっています。

29g511552242228

動いたので、UnityからWebGL出力

さて、エディター上では一通り、それなりの動きを見ることができました。
次はUnityからWebGL出力をします。これでブラウザ上で上手くシェーダーが動けばいいんですが・・・

書き出してしばし待機・・・

圧縮されてはいますが、動きました!

スクリーンショット 2015-11-25 18.48.59

デモはこちらになります。

モバイルブラウザでも動くの?

ちなみに、iOS9.1で動くのかも試してみましたが・・・
動きませんでした。まだ動かないかー。Android5.1でも動きませんでした。
しかも、こんなアラートも出ています。

IMG_0537

「まだUnityはWebGL出力をサポートしていないけどいいの?それでもいいならOKを押してくれたって構わないが・・」という相変わらずの内容です。当然OKを押しましたが、動きません。

モバイル対応の道はまだまだのようです。詳しい情報をご存知の方はお教えいただければと思います。

結果

GLSLで書いたシェーダーを、UnityのWebGL出力で、PCブラウザでは動かすことができました!(モバイルブラウザでは不可)
シェーダーを書くと、画面の表現力が一段増す感じがしますね。

【デモあり】Unity5のWebGLエクスポートをしてみました

GDC2015にて、Unity5の正式リリース発表がありました。
早速ダウンロードしてWebGLでエクスポートしてみました!
プラグイン不要ってすごい。

デモはこちら
URL : http://mankindinc.jp/wp01/games/carscenehtml5/

動いた・・・!

タップでCubeが出てくるようにしています。
ライトが非常に綺麗になりましたね。
あと、rigidbodyの処理が心なしか、上がってる感じがします・・・

WebGLということはiOSでも表示できるわけですよね。
iPhoneで実行してみると・・・

まだ対応してないようでした。