20200528のUnityに関する記事は8件です。

Unityで360度画像を表示する方法

今回はUnityで360度画像を取り入れる方法を見ていきます。 

事前に 360度画像を用意しておいてください。 

Unityのvrは2019.3を使用。

ステップ1 全球体のモデルと 360度画像をUnityのプロジェクトに入れる 

全球体のモデルは こちらを使うといいです。 

https://www.dropbox.com/s/f0cxi198g0i0mgf/Sphere100.fbx?dl=0

そしてこの spere100を シーンにドラッグして 

サイズを変更します。 

サイズは X =-100、Y =100、Z =100 ぐらいにする。

もしくはそれ以上。 

X軸は マイナスにして 反転を防ぎます。 

ステップ 2 画像を貼り付ける

次に 360度画像を spere100【球体】にドラッグして 

貼り付けます。 

貼り付けて うまく表示ができていない人は 

spere100の インスペクターから 

シェーダーを unit → textureにして 

その下にある 四角いマスの所に 画像を入れましょう。

bandicam 2020-05-28 21-27-58-822.jpg

これで 360度画像を表示できました。

ステップ3  ゲームビューでクルクル

せっかくなので 

ゲームビューで 視点をクルクル 見渡せるようにしていきましょう。 

これは StandardAssetに入っている 

rollerodyFPSControllerを使えば 簡単にできます。 

まず StandardAssetをインポートして 

その中に rollerodyFPSControllerのプレハブがあるので 

それをシーンにドラッグします。  

場所としては、 球体の真ん中ぐらいに配置するのがいいでしょう。 

そのままだと 落ちてしまうので 

インスペクターから リジッドボディ そして 

重力をOFFにします。 チェックをはずせばOKです。 

これで 再生してみると 

ゲームビューで360度 視点で見れます。 

このとき マウスカーソルが消えてしまいますが 

ESCキーを押せば 復活します。 

こんな感じに 配置するといい。

bandicam 2020-05-28 21-41-03-226.jpg

PS 

360度 動画にする方法はこちら

  

                                                              

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【一緒に始めるUnity】#1 はじまりのはじまり

脱出ゲームを作りたい

そんなわけでよく見かけるUnityに興味を持ちました。

解説というよりも「こっちは今こんな感じです」タイプの記事になると思います。
よかったら一緒にやってこ。

インストール

インストールはサクッと簡単だったので詳細は割愛。
ファイルをココからダウンロードして実行してね。指示通りに進めたら大丈夫(力強)。

チュートリアルをやってみる

ああい!全部英語だー!
unity-tutorial.png

日本語を選択した時の淡い期待が打ち砕かれた瞬間だったね。
初っ端から覚悟を強いられて試されてる感じある。

おわり

有給取ってやってやろうかと思ってるけど、どう?憧れのUnityを前にわくわくしてる?

じゃあみんな、チュートリアルを終えた世界でまたお会いしましょうね~!
以上【一緒に始めるUnity】#1 はじまりのはじまり でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity で xsd(xml Schema)ファイルを用いて Xml ファイルを検証する

本記事は Unity 2019.3.3f1 及び Microsoft Visual Studio Community 2019 version 16.5.4 を使用しております。

本記事の内容

タイトルの通り、xsd ファイルを用いて xmlファイルの検証をします。
第1項 では、外部 xml,xsd ファイルから読み込み、検証を行い、
第2項 では、Resource.Load を用いて検証を行います。

本記事で使う xsd,xml ファイルは以下になります。

Event.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="hogehoge/Event"
          elementFormDefault="qualified"
          xmlns="http://tempuri.org/XMLSchema.xsd"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:Event="hogehoge/Event"
          xmlns:Status="hogehoge/Status">
<xs:import schemaLocation="Status.xsd" namespace="hogehoge/Status"/>
  <xs:element name="Event">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Message">
          <xs:complexType mixed="true">
            <xs:attribute name="Name" type="Status:Name"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>
Status.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="hogehoge/Status"
          elementFormDefault="qualified"
          xmlns="http://tempuri.org/XMLSchema.xsd"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:Status="hogehoge/Status">

  <xs:simpleType name="Name">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Anyone"/>
      <xs:enumeration value="Someone" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

Message.xml
<?xml version="1.0" encoding="utf-8"?>
<Event xmlns="hogehoge/Event">
  <Message Name="Anyone">
    私はどこかにいるだれか
  </Message>
  <Message Name="Someone">
    僕はここにいるだれか
  </Message>
</Event>

1.外部 xsd,xml ファイルを読み込み、検証する

まず、外部 xsd,xml ファイルを読み込む場合には以下のようになります。

XmlGetter.cs
/// 変数の先頭の p は passed(引数)
/// l は local(メソッドスコープ内)
/// m は member(プロパティ)の略語です。

public static class XmlGetter
{
    public static XmlSchemaSet AddSchemaFromXsd(string pSchemaPath)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        XmlReader lSchemaReader = null;

        string targetNamespace = "hogehoge/Event";
        try
        {
            lSchemas.Add(targetNamespace, lSchemaReader = XmlReader.Create(pSchemaPath));

        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);
        }
        finally
        {
            if (lSchemaReader.ReadState != ReadState.Closed)
            {
                lSchemaReader.Close();
            }
        }
        return schemas;
    }

    public static XDocument GetXDocumentFromXml(string pXmlPath, XmlSchemaSet pSchemas)
    {

        XmlReaderSettings lSettings = new XmlReaderSettings();

        lSettings.ValidationType = ValidationType.Schema;

        lSettings.Schemas = pSchemas;

        XmlReader lXmlReader = null;
        XDocument pEventXml = null;
        try
        {
            lXmlReader = XmlReader.Create(pXmlPath, lSettings);
            pEventXml = XDocument.Load(lXmlReader);
        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);

        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
        }
        finally
        {
            if (lXmlReader.ReadState != ReadState.Closed)
            {
                lXmlReader.Close();
            }
        }
        return pEventXml;
    }
}
XmlGetTest.cs
public class XmlGetTest:MonoBehavior
{
    public void Awake()
    {
        string lSchemaPath = "Event.xsd";
        XmlSchemaSet lSchemas = AddSchemaFromXsd(lSchemaPath);
        string lXmlPath = "Event.xml";
        XDocument lXDocument = GetXDocumentFromXml(lXmlPath, lSchemas);
    }

}

これは、Unity のプロジェクトの外に XML,XSD ファイルが共にある場合に使えます。
その代わりに、プロジェクトをビルドした後に、自分でフォルダをビルドフォルダに加えなければいけません。
path は自分のファイルへのパスに適時置き換えて下さい。

xmlReaderSetting の ValidationType に Schema をセットすることで、Load 時に検証も行っています。

2.Resource.Load で xsd ファイルを読み込む

しかし、Unity で Resource フォルダ下に XML,XSD ファイルを置きたい場合があります。今回はその例を紹介します。

Resouce.Load を使う場合、そのファイル形式は Unity で対応していなければいけません。

2-1.ScriptedImporter を使う

Unity が対応していないファイルを使う場合、ScriptedImporter クラスを使います。

ScriptedImporter の詳しい説明はテラシュールブログさんを
参照して下さい。
テラシュールブログさん:
http://tsubakit1.hateblo.jp/entry/2017/12/14/012746

ScriptedImporter を使って xsd を TextAsset として扱うクラスを作ります。

XsdImporter.cs
[ScriptedImporter(1, "xsd")]
public class XSDImporter : ScriptedImporter
{
    public override void OnImportAsset(AssetImportContext ctx)
    {
        TextAsset lXsd;

        try
        {
            lXsd = new TextAsset( File.ReadAllText(ctx.assetPath) );
            ctx.AddObjectToAsset("XSD", lXsd);
            ctx.SetMainObject(lXsd);

        }
        catch (Exception e)
        {
            Debug.LogError(e.Message);
        }
    }
}

これで xsd ファイルを Resource.Load で読み込むことが出来るようになりました。

2-2.TextAsset を StringReader を経由して XmlReader インスタンスを生成する。

xsd ファイルを TextAsset として使う場合、StringReader クラスを使い、XmlReader インスタンスを生成します。

XmlGetter.cs
    public static XmlSchemaSet AddSchemaFromTextAsset(TextAsset pSchemaAsset)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        XmlReader lSchemaReader = null;
        StringReader lStringReader = null;
        string lTargetNamespace = "hogehoge/Event";
        try
        {
            lStringReader = new StringReader(pSchemaAsset.text);
            lSchemas.Add(lTargetNamespace, lSchemaReader =XmlReader.Create( lStringReader));

        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);
        }
        finally
        {
            if (lSchemaReader.ReadState != ReadState.Closed)
            {
                lSchemaReader.Close();
            }
        }
        return lSchemas;

    }

AddSchemaFromTextAsset メソッドが、渡された TextAsset の text プロパティを使った StringReader を経由して XmlReader インスタンスを作成し、 XmlSchemaSet に変換します。

xml も同様に TextAsset から StringReader を用いて XmlReader インスタンスを作成して下さい。

2-3. import する xsd ファイルを XmlSchemaSet クラスに Add する

しかし、ここでエラーが発生します。

StringReader を使って XmlReader を作成すると、import した xsd ファイルを SchemaSet に Add する事が出来ません。

そこで、少々周りくどいですが、一つづつ SchemaSet を Add していく手段を取ります。

ついでにパスを一つづつ設定するのが嫌なのでパスジェネレーターも作りました。

PathGenerator.cs
public class PathGenerator
{
    string mBasePath;
    public PathGenerator(string pBaseath)
    {
        mBasePath = pBasepath;
    }

    public string GeneratePath(params string[] pPathes)
    {
        StringBuilder lGeneratePath =new StringBuilder().Append(mBasePath);
        foreach (string path in pPathes)
        {
            lGeneratePath.Append("/").Append(path);
        }
        return lGeneratePath.ToString();
    }
}
XmlGetter.cs
public enum eSchema
{
    Event,
    Status
}

public class XmlGetter
{

    /// 中略
    /// SchemaPath は自分のスキーマフォルダへのパスを入れて下さい。

    public Dictionary<string,string> GeneratePathPair()
    {
        string lEventStr = eSchema.Event.ToString();
        string lStatusStr = eSchema.Status.ToString();

        PathGenerator lTnsGenerator = new PathGenerator("hogehoge");
        PathGenerator lPathGenerator = new PathGenerator(SchemaPath);
        Dictionary<string, string> lTnsPathPairInXsd = new Dictionary<string, string>
        {
            [lTnsGenerator.GeneratePath(lEvent)] = lPathGenerator.GeneratePath(lEvent),
            [lTnsGenerator.GeneratePath(lStatus)] =
lPathGenerator.GeneratePath(lStatus)
        };
        return lTnsPathPairInXsd;
    }

    public static XmlSchemaSet GetSchemaFromXSD(Dictionary<string,string> pXsd)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        StringReader lStringReader = null;
        XmlReader lSchemaReader = null;

        foreach (KeyValuePair<string, string> item in pXsd)
        {
            try
            {
                TextAsset xsdAsset = Resources.Load<TextAsset>(item.Value);
                lStringReader = new StringReader(xsdAsset.text);
                lSchemaReader = XmlReader.Create(lStringReader);
                lSchemas.Add(item.Key, lSchemaReader);

            }
            catch (XmlSchemaValidationException e)
            {
                Debug.LogError(e.Message);
                Debug.LogError("error line : " + e.LineNumber);
                Debug.LogError("error position : " + e.LinePosition);
            }
            finally
            {
                if (lStringReader != null)
                {
                    lStringReader.Close();
                }
                if (lSchemaReader.ReadState != ReadState.Closed)
                {
                    lSchemaReader.Close();

                }
            }
        }
        return lSchemas;
    }

}

何か指摘ありましたら宜しくお願いします。

参考にしたサイト。
xmlSchemaSet
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.schema.xmlschemaset?view=netcore-3.1

XmlReader
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.xmlreader?view=netcore-3.1

StringReader
https://docs.microsoft.com/ja-jp/dotnet/api/system.io.stringreader?view=netcore-3.1

XDocument
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.linq.xdocument?view=netcore-3.1

XmlSchema,Xml に関して参考にした書籍(「基礎 XML」著:山田祥寛)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity で xsd(xml Schema)ファイルを用いて xml ファイルを検証する

本記事は Unity 2019.3.3f1 及び Microsoft Visual Studio Community 2019 version 16.5.4 を使用しております。

本記事の内容

タイトルの通り、xsd ファイルを用いて xmlファイルの検証をします。
第1項 では、外部 xml,xsd ファイルから読み込み、検証を行い、
第2項 では、Resource.Load を用いて検証を行います。

本記事で使う xsd,xml ファイルは以下になります。

Event.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="hogehoge/Event"
          elementFormDefault="qualified"
          xmlns="http://tempuri.org/XMLSchema.xsd"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:Event="hogehoge/Event"
          xmlns:Status="hogehoge/Status">
<xs:import schemaLocation="Status.xsd" namespace="hogehoge/Status"/>
  <xs:element name="Event">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Message">
          <xs:complexType mixed="true">
            <xs:attribute name="Name" type="Status:Name"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>
Status.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="hogehoge/Status"
          elementFormDefault="qualified"
          xmlns="http://tempuri.org/XMLSchema.xsd"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:Status="hogehoge/Status">

  <xs:simpleType name="Name">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Anyone"/>
      <xs:enumeration value="Someone" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

Message.xml
<?xml version="1.0" encoding="utf-8"?>
<Event xmlns="hogehoge/Event">
  <Message Name="Anyone">
    私はどこかにいるだれか
  </Message>
  <Message Name="Someone">
    僕はここにいるだれか
  </Message>
</Event>

1.外部 xsd,xml ファイルを読み込み、検証する

まず、外部 xsd,xml ファイルを読み込む場合には以下のようになります。

XmlGetter.cs
/// 変数の先頭の p は passed(引数)
/// l は local(メソッドスコープ内)
/// m は member(プロパティ)の略語です。

public static class XmlGetter
{
    public static XmlSchemaSet AddSchemaFromXsd(string pSchemaPath)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        XmlReader lSchemaReader = null;

        string targetNamespace = "hogehoge/Event";
        try
        {
            lSchemas.Add(targetNamespace, lSchemaReader = XmlReader.Create(pSchemaPath));

        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);
        }
        finally
        {
            if (lSchemaReader.ReadState != ReadState.Closed)
            {
                lSchemaReader.Close();
            }
        }
        return schemas;
    }

    public static XDocument GetXDocumentFromXml(string pXmlPath, XmlSchemaSet pSchemas)
    {

        XmlReaderSettings lSettings = new XmlReaderSettings();

        lSettings.ValidationType = ValidationType.Schema;

        lSettings.Schemas = pSchemas;

        XmlReader lXmlReader = null;
        XDocument pEventXml = null;
        try
        {
            lXmlReader = XmlReader.Create(pXmlPath, lSettings);
            pEventXml = XDocument.Load(lXmlReader);
        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);

        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
        }
        finally
        {
            if (lXmlReader.ReadState != ReadState.Closed)
            {
                lXmlReader.Close();
            }
        }
        return pEventXml;
    }
}
XmlGetTest.cs
public class XmlGetTest:MonoBehavior
{
    public void Awake()
    {
        string lSchemaPath = "Event.xsd";
        XmlSchemaSet lSchemas = AddSchemaFromXsd(lSchemaPath);
        string lXmlPath = "Event.xml";
        XDocument lXDocument = GetXDocumentFromXml(lXmlPath, lSchemas);
    }

}

これは、Unity のプロジェクトの外に XML,XSD ファイルが共にある場合に使えます。
その代わりに、プロジェクトをビルドした後に、自分でフォルダをビルドフォルダに加えなければいけません。
path は自分のファイルへのパスに適時置き換えて下さい。

xmlReaderSetting の ValidationType に Schema をセットすることで、Load 時に検証も行っています。

2.Resource.Load で xsd ファイルを読み込む

しかし、Unity で Resource フォルダ下に XML,XSD ファイルを置きたい場合があります。今回はその例を紹介します。

Resouce.Load を使う場合、そのファイル形式は Unity で対応していなければいけません。

2-1.ScriptedImporter を使う

Unity が対応していないファイルを使う場合、ScriptedImporter クラスを使います。

ScriptedImporter の詳しい説明はテラシュールブログさんを
参照して下さい。
テラシュールブログさん:
http://tsubakit1.hateblo.jp/entry/2017/12/14/012746

ScriptedImporter を使って xsd を TextAsset として扱うクラスを作ります。

XsdImporter.cs
[ScriptedImporter(1, "xsd")]
public class XSDImporter : ScriptedImporter
{
    public override void OnImportAsset(AssetImportContext ctx)
    {
        TextAsset lXsd;

        try
        {
            lXsd = new TextAsset( File.ReadAllText(ctx.assetPath) );
            ctx.AddObjectToAsset("XSD", lXsd);
            ctx.SetMainObject(lXsd);

        }
        catch (Exception e)
        {
            Debug.LogError(e.Message);
        }
    }
}

これで xsd ファイルを Resource.Load で読み込むことが出来るようになりました。

2-2.TextAsset を StringReader を経由して XmlReader インスタンスを生成する。

xsd ファイルを TextAsset として使う場合、StringReader クラスを使い、XmlReader インスタンスを生成します。

XmlGetter.cs
    public static XmlSchemaSet AddSchemaFromTextAsset(TextAsset pSchemaAsset)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        XmlReader lSchemaReader = null;
        StringReader lStringReader = null;
        string lTargetNamespace = "hogehoge/Event";
        try
        {
            lStringReader = new StringReader(pSchemaAsset.text);
            lSchemas.Add(lTargetNamespace, lSchemaReader =XmlReader.Create( lStringReader));

        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);
        }
        finally
        {
            if (lSchemaReader.ReadState != ReadState.Closed)
            {
                lSchemaReader.Close();
            }
        }
        return lSchemas;

    }

AddSchemaFromTextAsset メソッドが、渡された TextAsset の text プロパティを使った StringReader を経由して XmlReader インスタンスを作成し、 XmlSchemaSet に変換します。

xml も同様に TextAsset から StringReader を用いて XmlReader インスタンスを作成して下さい。

2-3. import する xsd ファイルを XmlSchemaSet クラスに Add する

しかし、ここでエラーが発生します。

StringReader を使って XmlReader を作成すると、import した xsd ファイルを SchemaSet に Add する事が出来ません。

そこで、少々周りくどいですが、一つづつ SchemaSet を Add していく手段を取ります。

ついでにパスを一つづつ設定するのが嫌なのでパスジェネレーターも作りました。

PathGenerator.cs
public class PathGenerator
{
    string mBasePath;
    public PathGenerator(string pBaseath)
    {
        mBasePath = pBasepath;
    }

    public string GeneratePath(params string[] pPathes)
    {
        StringBuilder lGeneratePath =new StringBuilder().Append(mBasePath);
        foreach (string path in pPathes)
        {
            lGeneratePath.Append("/").Append(path);
        }
        return lGeneratePath.ToString();
    }
}
XmlGetter.cs
public enum eSchema
{
    Event,
    Status
}

public class XmlGetter
{

    /// 中略
    /// SchemaPath は自分のスキーマフォルダへのパスを入れて下さい。

    public Dictionary<string,string> GeneratePathPair()
    {
        string lEventStr = eSchema.Event.ToString();
        string lStatusStr = eSchema.Status.ToString();

        PathGenerator lTnsGenerator = new PathGenerator("hogehoge");
        PathGenerator lPathGenerator = new PathGenerator(SchemaPath);
        Dictionary<string, string> lTnsPathPairInXsd = new Dictionary<string, string>
        {
            [lTnsGenerator.GeneratePath(lEvent)] = lPathGenerator.GeneratePath(lEvent),
            [lTnsGenerator.GeneratePath(lStatus)] =
lPathGenerator.GeneratePath(lStatus)
        };
        return lTnsPathPairInXsd;
    }

    public static XmlSchemaSet GetSchemaFromXSD(Dictionary<string,string> pXsd)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        StringReader lStringReader = null;
        XmlReader lSchemaReader = null;

        foreach (KeyValuePair<string, string> item in pXsd)
        {
            try
            {
                TextAsset xsdAsset = Resources.Load<TextAsset>(item.Value);
                lStringReader = new StringReader(xsdAsset.text);
                lSchemaReader = XmlReader.Create(lStringReader);
                lSchemas.Add(item.Key, lSchemaReader);

            }
            catch (XmlSchemaValidationException e)
            {
                Debug.LogError(e.Message);
                Debug.LogError("error line : " + e.LineNumber);
                Debug.LogError("error position : " + e.LinePosition);
            }
            finally
            {
                if (lStringReader != null)
                {
                    lStringReader.Close();
                }
                if (lSchemaReader.ReadState != ReadState.Closed)
                {
                    lSchemaReader.Close();

                }
            }
        }
        return lSchemas;
    }

}

何か指摘ありましたら宜しくお願いします。

参考にしたサイト。
xmlSchemaSet
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.schema.xmlschemaset?view=netcore-3.1

XmlReader
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.xmlreader?view=netcore-3.1

StringReader
https://docs.microsoft.com/ja-jp/dotnet/api/system.io.stringreader?view=netcore-3.1

XDocument
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.linq.xdocument?view=netcore-3.1

XmlSchema,Xml に関して参考にした書籍(「基礎 XML」著:山田祥寛)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【DOTS】Havok Physicsを使ってみよ~

記事の環境

  • Unity 2019.3.0f6
  • Unity Hub 2.3.2
  • Havok Physics for Unity 0.2.2
  • Unity Physics 0.3.2
  • Hybrid Renderer 0.4.0

諸注意

この記事はUnityの公式ガイドに沿って書いてます。
わしは公式がええんじゃ!!という方は公式ガイドを推奨します。

Havok Physicsって??

アイルランドにあるHavok社が開発を行っている物理エンジンで、モバイルからコンシューマー、PCまで様々なタイトルで多数の採用実績があります。
現在はMicrosoftの傘下だそう。

そんな超有名物理エンジンがUnityで採用されることになったんだからやるしかねぇ!!ということでやっていきます。

[ミッション1] 公式のGitからサンプルコードを入手せよ!

  1. こ↑こ↓をクリック
  2. Clone or downloadをクリック
  3. Download ZIPをクリック

[ミッション2]解凍してUnity Hubに追加せよ!

  1. 先ほどダウンロードしたZipファイルを任意のフォルダに解凍
  2. Unity Hubを起動
  3. リストに追加をクリック
  4. 先ほど解凍したフォルダの中にあるUnityPhysicsSamplesというフォルダをクリックして、フォルダの選択をクリック
  5. 初期バージョンがない人は任意のUnityバージョンを指定(本記事ではUnity2019.3.0f1が無かったため3.0f6にアップグレードしました)

[ミッション3]Havok Physicsをプロジェクトに追加せよ!

  1. Unity Hubからプロジェクトをクリック(開くまで待つべし!待つべし!)
  2. Window > Package Managerの順でクリックしてPackage Managerを開く
  3. Advanced > Show preview packagesの順でクリックしてプレビューパッケージを表示するように
  4. 検索欄にHavokと入力
  5. 出てくるHavok Physics for Unityを選択して右下のInstallをクリック(インストールできるまで待つべし!待つべし!)

[ミッション4]まずはUnity Physicsを体感せよ!

  1. Assets / Tests / Pyramidsの中にあるPyramidsシーンをクリック
  2. HierarchyビューのPhysics Scene Basic ElementsにあるMain CameraのTransformを変更
    カメラ位置とかの説明
  3. できたら実行してみよう!
    ※一番最初の実行時はちょっと重くなる可能性あるので、重かったら一旦止めてもう一回実行してみて下さい。
    UnityPhysicsMovie.gif
    ふむ。。。
    特になんも力を加えてないのにピラミッドが崩れてしまった。。。。
    ※なんか赤い部分がチラチラ見えますがエンコが上手く行かなかったようで、、、実際は赤くないですw

[ミッション5]Havok Physicsを体感せよ!

  1. HierarchyビューのPhysics Scene Basic ElementsにあるPhysics Settingsをクリック
  2. そのオブジェクトにくっ付いてるPhysics StepコンポーネントにあるSimulation TypeをUnity PhysicsからHavok Physicsに変更
    Havok_physics選択.png
  3. できたら実行!!
    HavokPhysicsMovie.gif
    今度は触ってない時ちゃんとピラミッドは形を保っていますね。
    さすがHavok Physics!!
    20年以上に渡る歴史は伊達じゃないです。
    密になってるオブジェクトの山からブロックを引き抜く動作も見事に出来てます。(ジェンガとか作れそう)

    ※追記 2020/05/30 作ってみました。
    Gitリポジトリも置いておきます。

あとがき

いかがだったでしょうか?
個人的にはなかなか感動しましたw
こういうサンプルは他にもいっぱいあって、気になる方はAssets / LoaderSceneフォルダにあるLoaderSceneシーンファイルを実行してみて下さい。
ここから全サンプルへ飛ぶことが出来ます。
物理エンジンとしてのクオリティがどえりゃぁたけぇHavok Physicsですが将来的には有償での提供になるそうな。。?
Unity Physicsは物理エンジンとしての精度はHavok Physicsに劣りますが、ステートレス物理演算なことや100%C#で記述されたオープンソースなエンジンであること。
また無償で使用できることなどこの辺りでHavok Physicsと差別化を図っていくのではないかと思います。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでWatsonの音声認識と音声読み上げを使ってみる

はじめに

はじめして!Qiita初投稿になります!
よろしくお願いいたします。

本記事では、
UnityでのWatsonを使ったアプリケーションを作る際に、自分がわからなくて詰まった
Watson SDKにおけるSpeech To TextとText To Speechの認証方法と使い方について記述します。
これらサービスを使ったアプリケーションを作成しているので
以下のURLを参考にしてみてください。
https://github.com/sugarsolt/SecretaryUnity-chan

対象者

UnityのWatson SDKを使ってみたいけど、使い方がよくわからない人
(注)ただし、IBM Cloudへの登録方法は記述しません。

目次

インストール

Watson SDkのインストール方法は以下のURLにも記載されていますが、簡単に説明させていただきます。
https://github.com/watson-developer-cloud/unity-sdk
1. Unityでプロジェクトを作成
2. Api Compatibility Levelを.Net 4.xに変更
3. unity-sdkとunity-sdk-coreをダウンロード
4. unity-sdkとunity-sdk-coreを解凍
5. unity-sdkとunity-sdk-coreをAsset下に移動
以上でインストールは完了です。
ここで、私が使っている各ソフトウェアのバージョンを記載します。

  • Windows10
  • Unity 2019.3.12f1 (64-bit)
  • unity-sdk-core 1.2.0
  • unity-sdk 4.6.0

認証

この章では、Speech To TextとText To Speechサービスを使う上での認証用プログラムを示します。
ここはサンプルプログラムと同じです。

Speech To Text認証

これはSpeech To Textサービスの認証用のプログラムになります。
APIとURLを入力することによって認証を得ます。

Talk.cs
private IEnumerator authWatsonSTT(){
    //STT認証
    if (string.IsNullOrEmpty(STTIamApikey))
    {
        throw new IBMException("Plesae provide IAM ApiKey for the service.");
    }
    IamAuthenticator authenticator = new IamAuthenticator(apikey: STTIamApikey);
    //  Wait for tokendata
    while (!authenticator.CanAuthenticate()){
        yield return null;
    }
    STTService = new SpeechToTextService(authenticator);
    if (!string.IsNullOrEmpty(STTServiceUrl))
    {
        STTService.SetServiceUrl(STTServiceUrl);
    }
    yield break;
}

Text To Speech認証

これはText To Speechサービスの認証用のプログラムになります。
Speech To Text認証と同様にAPIとURLを入力することによって認証を得ます。

Talk.cs
private IEnumerator authWatsonTTS(){
    //TTS認証
    if (string.IsNullOrEmpty(TTSIamApikey))
    {
        throw new IBMException("Plesae provide IAM ApiKey for the service.");
    }
    IamAuthenticator authenticator = new IamAuthenticator(apikey: TTSIamApikey);
    //  Wait for tokendata
    while (!authenticator.CanAuthenticate()){
        yield return null;
    }
    TTSService = new TextToSpeechService(authenticator);
    if (!string.IsNullOrEmpty(TTSServiceUrl))
    {
        TTSService.SetServiceUrl(TTSServiceUrl);
    }
    yield break;
}

音声認識・音声読み上げ

この章では音声認識プログラムと音声読み上げプログラムについて記述いたします。
ここでは示さないその他のパラメータの指定方法はwatson sdkのサイトを参考にしてください。

Speech To Text

Speech To Textサービスを利用するためのプログラムについて記述します。
機能としては、watsonを使って、wav形式の音声データを文章に変換するものです。
このプログラムで指定する必要があるパラメータは”audio”、”contentType”、”model”になります。
”audio”では”contentType”で指定した形式のデータをbyte[]型で取得したものを渡すところになります。
”contentType”ではwavやmp3などのデータ形式を指定します。
”model”では日本語や英語などを聞き取るデータの言語を指定します。
ここでは日本語を聞き取るので”ja-JP_BroadbandModel”を指定しました。
コールバック関数の中では、送信した音声データを文字列に変換したデータを取得しています。

Talk.cs
private IEnumerator WatsonSTT()
{
    //認識開始 
    STTService.Recognize(callback: (DetailedResponse<SpeechRecognitionResults> response, IBMError error) =>
        {
            jsontext = response.Response;
        },
        audio: bytes,
        contentType: "audio/wav",
        model: "ja-JP_BroadbandModel"
    );
    yield break;
}

Text To Speech

Text To Speechサービスを利用するためのプログラムについて記述します。
機能としては、watsonを使って文章をwav形式の音声データに変換するものです。
このプログラムで指定する必要があるパラメータは”text”、”voice”、”accept”になります。
”text”では発声してほしい文字列を指定します。
”voice”では発声してほしい話者を指定します。
”accept”では発声データの形式を指定します。
コールバック関数の中では、取得したbyte[]型のデータをaudio clipに変換し、
それを再生しています。

Talk.cs
private IEnumerator WatsonTTS(string VoiceText){
    //認識開始 
    byte[] synthesizeResponse = null;
    clip = null;
    TTSService.Synthesize(callback: (DetailedResponse<byte[]> response, IBMError error) =>
        {
            synthesizeResponse = response.Result;
            clip = WaveFile.ParseWAV("myClip", synthesizeResponse);
            PlayClip(clip);
        },
        text: VoiceText,
        voice: "ja-JP_EmiV3Voice",
        accept: "audio/wav"
    );
    yield break;
}

最後に

アプリケーションでwatsonを使う際に詰まったところについて記述しました。
初投稿で分かりにくいところが多々あると思いますが生暖かい目で見守ってください。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【unity】ニフクラを使って無料でランキング機能を実装する方法

この記事をみてできること

※無料で
(1)ゲームにログインを機能をつける
(2)ランキング機能をつける
例:ゲームでキャンペーンをする際に、ある一定期間の中で順位を把握したい際に
メールアドレスやIDでアカウントを紐づけることができる

必要なもの

・PC
・Unity
・Visual Studio

やりかた

以下の記事を順番にやっていく。

(1)クイックスタート
https://mbaas.nifcloud.com/doc/current/introduction/quickstart_unity.html
(2)ログイン機能実装
https://mbaas.nifcloud.com/doc/current/tutorial/unity_login.html
(3)ハイスコアをサーバーに保存
https://mbaas.nifcloud.com/doc/current/tutorial/unity_highscore.html
(4)ハイスコアランキングをつくる
https://mbaas.nifcloud.com/doc/current/tutorial/unity_leaderboard.html

その他参考になった記事

(上記のURLはすべてこちらのチュートリアルの続きです。私は開発するにあたりチュートリアルをやっていなかったので、下記URLのソースを見ながら改修をおこないました。)

https://github.com/unity3d-jp-tutorials/2d-shooting-game/wiki/%E7%AC%AC12%E5%9B%9E-Wave%E3%82%925%E5%80%8B%E3%81%AB%E3%81%99%E3%82%8B%E3%80%81%E3%82%B9%E3%82%B3%E3%82%A2%E3%81%AE%E5%AE%9F%E8%A3%85

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでVR対応のどこでもドア実現を目指す その3 XR対応のOblique Near-Plane Clipping編

前回までのあらすじ

前回の記事
NonVRでどこでもドアのポータルより手前のオブジェクトを消したけど、VRで見たら消えてなかった。。。
ng vr clip.PNG

アジェンダ

  • なぜVRだとポータルより前のオブジェクトが消えなかったのか
  • VR対応のCalculateObliqueMatrix
  • 動作確認
  • 問題
  • 雑感

記事の最後にサンプルプロジェクトのgithubのリンクを張っておきます。

動作環境

  • Windows 10
  • Unity 2019.3.14f1
    • XR Plug-in + Mock HMD Loader
    • XR Plug-in + SteamVR Unity Plugin v2.6.0b1

注意事項

SteamVRデバイスはBeta版でのみ確認(XR PluginのSteamVR向けは2020/05/27時点でbetaのみ)。
正式版と動作が異なる可能性があります。

なぜVRだとポータルより前のオブジェクトが消えなかったのか

原因

理由は単純でVRで使用するCameraのViewMatrix, ProjectionMatrixが左目、右目で分けられているからです。
前回使用したcamera.projectionMatrixはNonVR(デスクトップ)向けのプロパティだったため、VRの場合は機能しないみたいです。

左目、右目のMatrixは以下のメソッドで設定することになります。

ViewMatrix, ProjectionMatrixは両目で異なるのか

異なります。
ViewMatrixは想像がつくと思いますが、左目と右目の位置が異なるため、座標変換行列も異なります。
ProjectionMatrixも同様に異なると考えたほうが良いです。
「視錐台の形は両目で同じっぽいから、ProjectionMatrixも両目で同じじゃないの?」って思う方がいるかもしれませんが、ProjectionMatrix自体もライブラリ依存やデバイス依存となります。
実際、OpenVR経由(非Unity経由)でHTC Viveで使用されるProjectionMatrixを取得したところ、右目と左目でProjectionMatrixは異なっていました。

以下は両目の視錐台が異なる場合のイメージです。

何が言いたいのかというと、「ちゃんと左右の目それぞれでMatrix計算しましょう」ということです!

VR対応のCalculateObliqueMatrix

残念ながらUnityのCamera.CalculateObliqueMatrixはNonVR向けしかないようです。。。
ということで用意しました。

    /// <summary>
    /// Oblique near-plane projection matrix を計算して返す
    /// </summary>
    /// 
    /// <param name="projectionMatrix">
    /// Near-Planeを傾ける対象のprojection matrix。
    /// </param>
    /// 
    /// <param name="clipPlane">
    /// clip planeを表すVector4 (Camera.CalculateObliqueMatrixの引数と同様)]
    /// </param>
    /// <returns> Oblique near-plane projection matrix </returns>
    public static Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projectionMatrix, Vector4 clipPlane)
    {
        Vector4 nearClipInProj = new Vector4(
            Mathf.Sign(clipPlane.x), Mathf.Sign(clipPlane.y),
            1.0f, 1.0f);
        Vector4 q = projectionMatrix.inverse * nearClipInProj;
        Vector4 c = clipPlane * (2.0F / (Vector4.Dot(clipPlane, q)));

        projectionMatrix.SetRow(2, c - projectionMatrix.GetRow(3));

        return projectionMatrix;
    }

以下のコードを引用させていただきました。
Oblique near-plane clipping (Unity forums)

原理を知りたい方は以下の論文が参考になります。
Oblique View Frustum Depth Projection and Clipping


CalculateObliqueMatrix 改変版

逆行列計算が省略されたバージョンです。
Modifying the Projection Matrix to Perform Oblique Near-Plane Clipping より引用、改変させていただきました。

    public static Matrix4x4 CalculateObliqueMatrixSimple(Matrix4x4 projectionMatrix, Vector4 clipPlane) {
        Vector4 q = new Vector4(
          (Mathf.Sign(clipPlane.x) + projectionMatrix.m02) / projectionMatrix.m00,
          (Mathf.Sign(clipPlane.y) + projectionMatrix.m12) / projectionMatrix.m11,
          -1.0f,
          (1.0f + projectionMatrix.m22) / projectionMatrix.m23
        );

        Vector4 c = clipPlane * (2.0f / Vector4.Dot(clipPlane, q));
        c.z += 1.0f;

        projectionMatrix.SetRow(2, c);

        return projectionMatrix;
    }


使い方はProjectionMatrixを渡すこと以外はCamera.CalculateObliqueMatrixと同様です。


使い方のイメージ

以下のUpdateStereoProjectionMatrixに"Camera"と"clipPlaneとして使うTransform"を渡してください。

※clipPlaneとしてTransformを使う話は前回の記事を参照ください。

    private static void UpdateStereoProjectionMatrix(Camera camera, Transform clipPlaneTransform) {
        // Reset customized projection matrix
        camera.ResetStereoProjectionMatrices();

        // Need both projection matrices to be calculated.
        // If one matrix is changed, the other is changed to unit.
        Matrix4x4 leftProjMatrix = CalculateObliqueMatrix(camera, Camera.StereoscopicEye.Left, clipPlaneTransform);
        Matrix4x4 RightProjMatrix = CalculateObliqueMatrix(camera, Camera.StereoscopicEye.Right, clipPlaneTransform);

        camera.SetStereoProjectionMatrix(Camera.StereoscopicEye.Left, leftProjMatrix);
        camera.SetStereoProjectionMatrix(Camera.StereoscopicEye.Right, RightProjMatrix);
    }

    private static Matrix4x4 CalculateObliqueMatrix(Camera camera, Camera.StereoscopicEye eye, Transform clipPlaneTransform) {

        // Require the projection matrix of the specified eye to be reset previously
        // but it is impossible because of no method to reset only either,
        // so this method should be private for now.

        Matrix4x4 baseViewMatrix = camera.GetStereoViewMatrix(eye);
        Matrix4x4 baseProjMatrix = camera.GetStereoProjectionMatrix(eye);

        Vector3 clipPlaneNormal = baseViewMatrix.MultiplyVector(clipPlaneTransform.forward);
        Vector3 clipPlanePosition = baseViewMatrix.MultiplyPoint(clipPlaneTransform.position);

        Vector4 clipPlane = CalculateClipPlane(clipPlaneNormal, clipPlanePosition);

        return CalculateObliqueMatrix(baseProjMatrix, clipPlane);
    }


動作確認

前回と同様にVRを使って青の空間にあるポータルから赤の空間をのぞいてみましょう!
赤の空間にあるカメラとポータルの間にある白球は描画されないはずです。

image.png
image.png

結果

image.png
image.png

違う角度から見ても、白球は描画されなくなりました!

※もし実機で動作がおかしければコメントいただければと思います

問題

ここで1つ問題があります。

Multi Pass以外では Oblique Near-Plane Clipping は使えない

実はCamera.SetStereoProjectionMatrixはRenderModeがMulti Pass以外では機能しません!
Single Pass Instancedはパフォーマンス面で何かとメリットがあるので、これは結構致命的な問題かも。。。

ちなみにSingle Pass Instancedの状態で上記メソッドをコールすると以下の警告ログが出ます。


警告ログ
Can't set custom eye projection matrix when not in multipass mode
UnityEngine.Camera:SetStereoProjectionMatrix(StereoscopicEye, Matrix4x4)


雑感

まさかの3年越しの更新です。もし続きを待っていた方がいらっしゃたら本当にごめんなさい。。。
Unity 5.6.1f1からUnity 2019.3.14f1になっていますね(バージョンの表記方法すら違うw)。

UnityのVR対応もXR Pluginというものに移行するようですが、SteamVR向けの正式バージョンが出なくてつらいです。
仕方ないのでOculusかWindowsMRあたりの購入を考えたんですが、コロナ事情のせいか在庫がまったくなく。。。

あと、Single Pass InstancedでProjectionMatrixが改変できないのは、個人的にすごく残念に感じています。
代替手段が判明したら、共有しようと思います。

この3年間、記事は書かなかったんですがUnity自体には触れていました。
そして、今は「Depth Maskって柔軟性が微妙かな」って結論になっています。
もちろん、グラフィックメモリの消費が少なくて済むなどのメリットはあると思いますが、ポータルにエフェクトを入れたい場合はRenderTextureに落としたほうが何かと都合がいいかなって思っています。

さて、次は趣向を変えてFrustum Cullingを記事にしようと思います。
ClipPlaneを傾けていますので、何か悪影響が出ていないかが気になっています。

それでは!

サンプルプロジェクト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む