takataka430’s blog

.NET系を中心に勉強したことのまとめを書きます

MRTKとLineRendererを使ってオブジェクト間を線で結ぶ

今回はUnityのLineRendererという機能を使い、オブジェクト同士を線で結ぶ方法を調べてみました。
せっかくなので、MRTKを用いて3Dオブジェクトを移動させても維持するようにしてみました。

docs.unity3d.com

環境

Unity 2020.3.01f1
MRTK ver 2.7.2.0

手順

まずは以下のようなスクリプトを作成します。

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    public GameObject object1;
    public GameObject object2;

    Vector3[] positions;
    LineRenderer lineRenderer;

    void Start()
    {
        lineRenderer = gameObject.AddComponent<LineRenderer>();

        //オブジェクトの座標を取得
        positions = new Vector3[]
        {
            object1.transform.position,
            object2.transform.position
        };

        //線の太さを設定
        lineRenderer.startWidth = 0.005f;
        lineRenderer.endWidth = 0.005f;


        //マテリアルの設定
        var material = new Material(Shader.Find("Standard"));
        lineRenderer.material = material;

        //座標間に線を結ぶ
        lineRenderer.SetPositions(positions);
    }

    void Update()
    {
        positions[0] = object1.transform.position;
        positions[1] = object2.transform.position;
        lineRenderer.SetPositions(positions);
    }
}

次にUnityで3Dオブジェクトを作成していきます。下の写真のGameObjectはHierarchy画面で右クリックしてCreate Emptyから、Cube1Cube2は3D Object→Cubeから作成しました。

f:id:takataka430:20211011225916p:plain

GameObjectには上記で作成したスクリプトを追加し、Object1とObject2にそれぞれCube1とCube2を挿入しておきます。

f:id:takataka430:20211011230117p:plain

Cube1とCube2にはNearInteractionGrabbableとObject Manipulatorの2つのスクリプトを追加しておきます。

f:id:takataka430:20211011230231p:plain

以上で準備は完了です。
Unityのエディタでデバッグすると以下のように3Dオブジェクト間に線を引いてくれます。

f:id:takataka430:20211011231704g:plain

Azure Speech Serviceの発音評価機能を試す

Speech Serviceに発音評価機能があったので試してみました。

docs.microsoft.com

環境

Microsoft Visual Studio Community 2019 Version 16.11.2
Microsoft.CognitiveServices.Speech 1.18.0

コード

C#のコンソールアプリケーションを作成し、Nuget パッケージマネージャー からMicrosoft.CognitiveServices.Speechをインストールします。
コードは以下のようにします。

using Microsoft.CognitiveServices.Speech;
using Microsoft.CognitiveServices.Speech.Audio;
using Microsoft.CognitiveServices.Speech.PronunciationAssessment;
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {

        static async Task Main(string[] args)
        {
            Console.WriteLine("評価したいテキストを入力してください");
            var refText = Console.ReadLine();

            var speechConfig = SpeechConfig.FromSubscription("[Subscription Key]", "[Region]");

            var audioConfig = AudioConfig.FromDefaultMicrophoneInput();

            var pronunciationAssessmentConfig = new PronunciationAssessmentConfig(
                refText, 
                //スコアを100点満点で表示
                GradingSystem.HundredMark);

            using (var recognizer = new SpeechRecognizer(speechConfig, audioConfig))
            {
                pronunciationAssessmentConfig.ApplyTo(recognizer);
                Console.WriteLine("評価を開始します 録音中・・・");

                //録音開始
                var speechRecognitionResult = await recognizer.RecognizeOnceAsync();

                //評価結果の取得
                var pronunciationAssessmentResult =
                        PronunciationAssessmentResult.FromResult(speechRecognitionResult);
                var fluencyScore = pronunciationAssessmentResult.FluencyScore;
                var pronunciationScore = pronunciationAssessmentResult.PronunciationScore;

                Console.WriteLine("流暢さ:" + fluencyScore);
                Console.WriteLine("スコア:" + pronunciationScore);
            }
        }
    }
}

これを実行すると以下のようになります。

評価したいテキストを入力してください
Hello
評価を開始します 録音中・・・
流暢さ:100
スコア:98.8

今回はHelloという単語を評価してもらうように入力しました。(上記の2行目)
テキストを入力すると録音が開始されます。マイクに向かって声を入力してから評価がでるまで少し時間がかかります。
今回は流暢さとスコアを表示してみました。面白い機能ですね。

MRTKで手の動きを記録して再生する

MRTKのドキュメントを眺めていたらこんな機能を見つけました。

docs.microsoft.com

頭と手の動きを記録して再生できるらしいです。これは面白そう!
早速試してみましょう。

開発環境

Unity 2020.3.0f1
MRTK 2.7.2

スクリプトの作成

記録用のスクリプトは「InputRecordingControls」がMRTKに含まれています。

再生用のスクリプトは以下のようになります。

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.Input;
using System.IO;
using System.Linq;
using UnityEngine;

public class LoadAnimation : MonoBehaviour
{

    //記録したアニメーションを再生するためのフィールドを取得
    private InputPlaybackService playbackService = null;
    private InputPlaybackService PlaybackService
    {
        get
        {
            if (playbackService == null)
            {
                playbackService = CoreServices.GetInputSystemDataProvider<IMixedRealityInputPlaybackService>() as InputPlaybackService;
            }

            return playbackService;
        }
    }

    //再生したファイルを保存したフォルダの最新のファイルを取得して再生
    public void PlayUserInput()
    {
        if (PlaybackService != null)
        {
            var dirInfo = new DirectoryInfo("[記録したアニメーションファイルの保存フォルダ]");
            string filePath = dirInfo.GetFiles().OrderByDescending(f => f.LastWriteTime).FirstOrDefault().FullName;
            PlaybackService.LoadInputAnimation(filePath);
            PlaybackService.Play();
        }
        
    }

    void Start()
    {
        
    }

    void Update()
    {
        
    }
}

InputRecordingControlsで記録したファイルは、PCで実行した場合はC:\Users\[ユーザー名]\AppData\LocalLow\DefaultCompany\[アプリケーション名]という場所のフォルダに保存されます。これをnew DirectoryInfoの引数に指定します。

UIの作成

記録用と再生用の2つのボタン(PressableButtonHoloLens2)を作ります。 それぞれButton_RecordButton_Loadに名前を変えています。
次にそれぞれのボタンにスクリプトを張り付けて設定を行います。

記録用のボタン

Add Componentから「InputRecordingControls」を探して追加します。
On Recording Stopped()で+ボタンを押してイベントを追加します。左下の空欄にはこのボタンを挿入します。右上のNo Functionと書いてあるところにはInputRecordingControls→SaveRecordedInputを選択します。
以上の操作によって記録が終了したときにファイルが保存されます。
f:id:takataka430:20210904145622p:plain

次にInteractableのOnClick()を設定します。左下のNone(Object)のこのボタンを挿入します。右上のNo Functionと書いてあるところにはInputRecordingControls→ToggleRecordingを選択します。こうすると、ボタンを押すごとに記録の開始、終了の操作をすることができます。 f:id:takataka430:20210904145646p:plain

再生用のボタン

Add Componentから先ほど作成した再生用のスクリプトを挿入します。(今回はLoadAnimationという名前にしています)
次にInteractableのOnClick()を設定します。左下のNone(Object)のこのボタンを挿入します。右上のNo Functionと書いてあるところにはLoadAnimation→PlayUserInputを選択します。 f:id:takataka430:20210904145715p:plain

Unityのエディタ上で実行

f:id:takataka430:20210904145025g:plain 左のボタンを押すと記録が開始され、もう一度押すと記録終了です。右側のボタンを押すと記録された手の動きが再生されます。

Azure Communication Servicesのクイックスタート(チャット)をやってみた

チャットアプリを作ってみたいと思ったので、Azure Communication Servicesのチャットについて調べてみました。今回はコンソールアプリを用いたクイックスタートを使ってみます。

開発環境

Microsoft Visual Studio Community 2019 Version 16.10.3
Azure.Communication.Chat 1.0.1
Azure.Communication.Identity 1.0.1

AzureポータルでCommunication Servicesのリソース作成

手順についてはドキュメントに詳細が載っているので割愛します。 docs.microsoft.com

リソースを作成したら、左メニューからキーをクリックして、エンドポイント接続文字列をメモしておきます。(この後で使います)

f:id:takataka430:20210730234951p:plain

クライアントアプリの開発

Visual Studioでコンソールアプリを作成し、Program.csに以下のように記述します。
以下の2つの公式ドキュメントを参考に組み合わせました。

docs.microsoft.com

docs.microsoft.com

using Azure;
using Azure.Communication;
using Azure.Communication.Chat;
using Azure.Communication.Identity;
using System;

namespace ConsoleApp1
{
    class Program
    {

        static async System.Threading.Tasks.Task Main(string[] args)
        {
            // エンドポイントの設定
            Uri endpoint = new Uri("[エンドポイント]");

            var client = new CommunicationIdentityClient("[接続文字列]");
                
            //IDの作成
            var identityResponse = await client.CreateUserAsync();
            var identity = identityResponse.Value;
                
            //アクセストークンの取得
            var tokenResponse = await client.GetTokenAsync(identity, scopes: new[] { CommunicationTokenScope.Chat });
            var token = tokenResponse.Value.Token;

            //チャットクライアントの作成
            CommunicationTokenCredential communicationTokenCredential = new CommunicationTokenCredential(token);
            ChatClient chatClient = new ChatClient(endpoint, communicationTokenCredential);

            //参加者の作成
            var chatParticipant = new ChatParticipant(identifier: new CommunicationUserIdentifier(id: identity.Id))
            {
                DisplayName = "User01"
            };

            //スレッドの作成
            CreateChatThreadResult createChatThreadResult = await chatClient.CreateChatThreadAsync(topic: "Hello world!", participants: new[] { chatParticipant });
            ChatThreadClient chatThreadClient = chatClient.GetChatThreadClient(threadId: createChatThreadResult.ChatThread.Id);

            //メッセージの送信
            SendChatMessageResult sendChatMessageResult = await chatThreadClient.SendMessageAsync(content: "hello world", type: ChatMessageType.Text, senderDisplayName: chatParticipant.DisplayName);
            string messageId = sendChatMessageResult.Id;

            //メッセージの表示
            AsyncPageable<ChatMessage> allMessages = chatThreadClient.GetMessagesAsync();
            await foreach (ChatMessage message in allMessages)
            {
                Console.WriteLine($"{message.Id}:{message.Content.Message} by {message.SenderDisplayName}");
            }
        }
    }
}

これを実行すると以下のようになります。

1627655746772:hello world by User01
1627655746299: by
1627655746269: by

message Id、メッセージ、ユーザー名の順に表示しています。
メッセージは1回しか送っていないのに結果は3つありますね。なぜなんでしょうか?

とりあえず動きはなんとなくわかったので、今後は2つのアプリ間でチャットをできるようにする方法を調べてみようと思います。

Blazor WebAssemblyを使ってみた

以前から気になっていたBlazorの勉強を始めました。今回はプロジェクトを新規に作成して動かすところまでをやってみたいと思います。

環境

Visual Studio 2019

プロジェクトの作成手順

Visual Studio 2019を開き、「新しいプロジェクトの作成」をクリックします。

f:id:takataka430:20210717162909p:plain

「Blazor WebAssembly」で検索を行うと「Blazor WebAssemblyアプリ」が出てくるので、これを選択して右下の「次へ」をクリックします。

f:id:takataka430:20210717162938p:plain

プロジェクト名と保存場所を設定して「次へ」をクリックします。

f:id:takataka430:20210717163000p:plain

追加情報を設定できますが、今回は何も変更せずに「作成」をクリックします。

f:id:takataka430:20210717163016p:plain

プロジェクトが作成されます。画面上部の「IIS Express」をクリックするとアプリケーションが起動します。

f:id:takataka430:20210717163034p:plain

実行すると以下のようなアプリを利用することができます。ホームのページ以外にも、ボタンを押したら数字が増えるページや表が掲載されているページが実装されています。 f:id:takataka430:20210717163053g:plain

コードを見てみる

動きが分かったところで「Counter」ページのコードを見てみましょう。

f:id:takataka430:20210717163344p:plain

上半分が画面のレイアウト、下半分がコードになっています。
コードの部分はcurrentCountが画面に数字を表示するためのフィールドで、IncrementCountを実行するとcurrentCountが増えていきます。
画面のレイアウトでは、@onclickでボタンを押したときのイベントを定義しており、IncrementCountを実行します。currentCountの値が変更されると、5行目の@currentCountに反映される、という動きになっているようです。

使ってみた感想

C#経験はあるけどあまりフロントエンドをやったことがない自分にはとても親しみやすいと感じました。もっとたくさん使っていこうと思います。

ASP.NET Coreで接続文字列を使う

ASP.NET Coreを使っていて、接続文字列の扱いがよくわからなかったので調べてみました。

背景

  • ASP.NET Core でWeb APIを作りたい
  • Azure SQL Databseにつなぎたいので接続文字列が必要
  • 開発はローカルで行い、本番はAzureのApp Serviceへデプロイしたい
  • 接続文字列は
    • ソースコードに書きたくない
    • ローカルと App Serviceへのデプロイ後で別のものを使いたい

調べた結果

接続文字列を取得するコードは、例えば以下のようになります。

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace WebApplication1.Controllers
{
    [Route("api/test")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private IConfiguration _configuration;

        public TestController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [HttpGet]
        public string Get()
        {
            //「test」という名前の接続文字列がsに入る
            var s = _configuration.GetConnectionString("test");
            return s;
        }
    }
}

では、接続文字列はどこに記載すればいいのでしょうか。

ローカルの場合

プロジェクト内にあるappsettings.jsonに以下のように記述すると「これはローカルです」という文字列が取得できます。

{
  "ConnectionStrings": {
    "test": "これはローカルです" 
  }
}

また、もう一つの方法としてsecrets.jsonを使う方法があります。 Visual Studio 2019であれば、プロジェクトを右クリックして「ユーザーシークレットの管理」をクリックすると開くことができます。
使い方はappsettings.jsonと同じです。 このファイルはプロジェクトとは別の場所(C:\Users\[username]\AppData\Roaming\Microsoft\UserSecrets\[id]\secrets.json)にあります。

secret.jsonの方がソースコードに含まれないので安心して使えると思います。

App Service(Web Apps)の場合

「構成」を開き、画面下の「接続文字列」という場所があります。「新しい接続文字列」から名前と値を登録しましょう。
こうすると、AppService側でローカルとは異なる接続文字列を設定することができます。

f:id:takataka430:20210529155144p:plain

以上により、ローカル環境とAppService環境で接続文字列を使い分けることができます。

参考

Azure Web Apps の環境変数の管理の基本 ( Key Vault と User Secrets ) - BEACHSIDE BLOG

HoloLensなしでもMRを楽しめるツール・サービス

MRで遊びたいけどHoloLens 2がない、という場合でも案外使えるものがあるのでまとめてみました。

Mixed Reality Toolkit

MRアプリを作るためのUIツールです。クロスプラットフォームなのでHoloLens以外にも対応しています。

以下はOculus Quest用の設定方法です。自分はOculus Quest 2で試してみましたが、楽しいのでおすすめです。

docs.microsoft.com

iOSAndroidにも対応しているようです。(こちらは未検証)

docs.microsoft.com

Azure Spatial Anchors

アンカー情報をデバイス間で共有するためのサービスです。
以下のチュートリアルを進めていき、5番目にiOSAndroidでビルドする方法が書かれています。

docs.microsoft.com

Azure Remote Rendering

高品質な3Dオブジェクトをクラウドレンダリングするサービスです。これによって端末のスペック以上の表現が可能になります。

docs.microsoft.com

以下のサンプルはUnityエディター上で動かすことができます。面白いです。
docs.microsoft.com