HoloLens(1세대) 및 Azure 312: 봇 통합
참고
Mixed Reality Academy 자습서는 HoloLens(1세대)와 Mixed Reality 몰입형 헤드셋을 염두에 두고 설계되었습니다. 따라서 이러한 디바이스 개발에 대한 지침을 여전히 찾고 있는 개발자를 위해 이러한 자습서를 그대로 두는 것이 중요하다고 생각합니다. 이러한 자습서는 HoloLens 2 사용되는 최신 도구 집합 또는 상호 작용으로 업데이트되지 않습니다. 지원되는 디바이스에서 계속 작동하도록 유지 관리됩니다. HoloLens 2 위해 개발하는 방법을 보여주는 새로운 자습서 시리즈가 미래에 게시될 예정입니다. 이 알림은 해당 자습서가 게시될 때 해당 자습서에 대한 링크로 업데이트됩니다.
이 과정에서는 Microsoft Bot Framework V4를 사용하여 봇을 만들고 배포하고 Windows Mixed Reality 애플리케이션을 통해 통신하는 방법을 알아봅니다.
Microsoft Bot Framework V4는 확장 가능하고 확장 가능한 봇 애플리케이션을 빌드하는 도구를 개발자에게 제공하도록 설계된 API 집합입니다. 자세한 내용은 Microsoft Bot Framework 페이지 또는 V4 Git 리포지토리를 참조하세요.
이 과정을 완료한 후에는 다음을 수행할 수 있는 Windows Mixed Reality 애플리케이션을 빌드하게 됩니다.
- 탭 제스처를 사용하여 사용자 음성을 수신 대기하는 봇을 시작합니다.
- 사용자가 무언가를 말하면 봇은 응답을 제공하려고 시도합니다.
- Unity 장면에 봇 근처에 배치된 텍스트로 봇 회신을 표시합니다.
애플리케이션에서 결과를 디자인과 통합하는 방법은 사용자에게 달려 있습니다. 이 과정은 Azure 서비스를 Unity 프로젝트와 통합하는 방법을 가르치기 위해 고안되었습니다. 혼합 현실 애플리케이션을 향상시키기 위해 이 과정에서 얻은 지식을 사용하는 것이 사용자의 작업입니다.
디바이스 지원
코스 | HoloLens | 몰입형 헤드셋 |
---|---|---|
MR 및 Azure 312: 봇 통합 | ✔️ | ✔️ |
참고
이 과정은 주로 HoloLens에 중점을 두지만, 이 과정에서 배운 내용을 VR(몰입형) 헤드셋에 Windows Mixed Reality 적용할 수도 있습니다. 몰입형(VR) 헤드셋에는 접근성 있는 카메라가 없으므로 PC에 연결된 외부 카메라가 필요합니다. 과정을 진행하면서 몰입형(VR) 헤드셋을 지원하기 위해 사용해야 할 수 있는 변경 내용에 대한 메모를 볼 수 있습니다.
필수 조건
참고
이 자습서는 Unity 및 C#에 대한 기본적인 경험이 있는 개발자를 위해 설계되었습니다. 또한 이 문서의 필수 구성 요소와 서면 지침은 작성 당시 테스트 및 확인된 내용(2018년 7월)을 나타냅니다. 이 과정의 정보가 아래에 나열된 것보다 최신 소프트웨어에서 찾을 수 있는 것과 완벽하게 일치한다고 가정해서는 안 되지만 , 도구 설치 문서에 나열된 대로 최신 소프트웨어를 자유롭게 사용할 수 있습니다.
이 과정에는 다음 하드웨어 및 소프트웨어를 사용하는 것이 좋습니다.
- 몰입형(VR) 헤드셋 개발을 위해 Windows Mixed Reality 호환되는 개발 PC
- 개발자 모드를 사용하도록 설정된 Windows 10 Fall Creators Update 이상
- 최신 Windows 10 SDK
- Unity 2017.4
- Visual Studio 2017
- 개발자 모드가 설정된 Windows Mixed Reality 몰입형(VR) 헤드셋 또는 Microsoft HoloLens
- Azure 및 Azure Bot 검색에 대한 인터넷 액세스 자세한 내용은 이 링크를 따르세요.
시작하기 전에
- 이 프로젝트를 빌드하는 데 문제가 발생하지 않도록 하려면 이 자습서에 언급된 프로젝트를 루트 또는 루트에 가까운 폴더에 만드는 것이 좋습니다(긴 폴더 경로는 빌드 타임에 문제를 일으킬 수 있습니다).
- HoloLens를 설정하고 테스트합니다. HoloLens 설정을 지원해야 하는 경우 HoloLens 설정 문서를 방문해야 합니다.
- 새 HoloLens 앱 개발을 시작할 때 보정 및 센서 튜닝을 수행하는 것이 좋습니다(때로는 각 사용자에 대해 이러한 작업을 수행하는 데 도움이 될 수 있음).
보정에 대한 도움말은 HoloLens 보정 문서에 대한 이 링크를 따르세요.
센서 튜닝에 대한 도움말은 HoloLens 센서 튜닝 문서에 대한 이 링크를 따르세요.
1장 - 봇 애플리케이션 만들기
첫 번째 단계는 로컬 ASP.Net Core 웹 애플리케이션으로 봇을 만드는 것입니다. 완료하고 테스트한 후에는 Azure Portal에 게시합니다.
Visual Studio를 엽니다. 새 프로젝트를 만들고, 프로젝트 유형으로 ASP NET Core 웹 애플리케이션 을 선택하고(하위 섹션 .NET Core에서 찾을 수 있습니다) MyBot이라고 합니다. 확인을 클릭합니다.
표시되는 창에서 빈 을 선택합니다. 또한 대상이 ASP NET Core 2.0으로 설정되고 인증이 인증 없음으로 설정되어 있는지 확인합니다. 확인을 클릭합니다.
이제 솔루션이 열립니다. 솔루션 탐색기 솔루션 Mybot을 마우스 오른쪽 단추로 클릭하고 솔루션용 NuGet 패키지 관리를 클릭합니다.
찾아보기 탭에서 Microsoft.Bot.Builder.Integration.AspNet.Core를 검색합니다(시험판 포함이 선택되어 있는지 확인). 패키지 버전 4.0.1-preview를 선택하고 프로젝트 상자를 선택합니다. 그런 다음 설치를 클릭합니다. 이제 Bot Framework v4에 필요한 라이브러리를 설치했습니다. NuGet 페이지를 닫습니다.
솔루션 탐색기프로젝트MyBot을 마우스 오른쪽 단추로 클릭하고클래스추가|를 클릭합니다.
클래스 이름을 MyBot으로 지정하고 추가를 클릭합니다.
이전 점을 반복하여 ConversationContext라는 다른 클래스를 만듭니다.
솔루션 탐색기 wwwroot를 마우스 오른쪽 단추로 클릭하고새 항목추가|를 클릭합니다. HTML 페이지를 선택합니다(하위 섹션 웹에서 찾을 수 있습니다). 파일 이름을 로default.html. 추가를 클릭합니다.
솔루션 탐색기 클래스/개체 목록은 아래 이미지와 같습니다.
ConversationContext 클래스를 두 번 클릭합니다. 이 클래스는 대화의 컨텍스트를 유지하기 위해 봇에서 사용하는 변수를 보유합니다. 이러한 대화 컨텍스트 값은 활동을 받을 때마다 MyBot 클래스의 instance 새로 고쳐지므로 이 클래스의 instance 유지됩니다. 클래스에 다음 코드를 추가합니다.
namespace MyBot { public static class ConversationContext { internal static string userName; internal static string userMsg; } }
MyBot 클래스를 두 번 클릭합니다. 이 클래스는 고객의 들어오는 활동에 의해 호출되는 처리기를 호스트합니다. 이 클래스에서는 봇과 고객 간의 대화를 빌드하는 데 사용되는 코드를 추가합니다. 앞에서 설명한 것처럼 활동을 받을 때마다 이 클래스의 instance 초기화됩니다. 이 클래스에 다음 코드를 추가합니다.
using Microsoft.Bot; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; using System.Threading.Tasks; namespace MyBot { public class MyBot : IBot { public async Task OnTurn(ITurnContext context) { ConversationContext.userMsg = context.Activity.Text; if (context.Activity.Type is ActivityTypes.Message) { if (string.IsNullOrEmpty(ConversationContext.userName)) { ConversationContext.userName = ConversationContext.userMsg; await context.SendActivity($"Hello {ConversationContext.userName}. Looks like today it is going to rain. \nLuckily I have umbrellas and waterproof jackets to sell!"); } else { if (ConversationContext.userMsg.Contains("how much")) { if (ConversationContext.userMsg.Contains("umbrella")) await context.SendActivity($"Umbrellas are $13."); else if (ConversationContext.userMsg.Contains("jacket")) await context.SendActivity($"Waterproof jackets are $30."); else await context.SendActivity($"Umbrellas are $13. \nWaterproof jackets are $30."); } else if (ConversationContext.userMsg.Contains("color") || ConversationContext.userMsg.Contains("colour")) { await context.SendActivity($"Umbrellas are black. \nWaterproof jackets are yellow."); } else { await context.SendActivity($"Sorry {ConversationContext.userName}. I did not understand the question"); } } } else { ConversationContext.userMsg = string.Empty; ConversationContext.userName = string.Empty; await context.SendActivity($"Welcome! \nI am the Weather Shop Bot \nWhat is your name?"); } } } }
Startup 클래스를 두 번 클릭합니다. 이 클래스는 봇을 초기화합니다. 클래스에 다음 코드를 추가합니다.
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Bot.Builder.BotFramework; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace MyBot { public class Startup { public IConfiguration Configuration { get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton(_ => Configuration); services.AddBot<MyBot>(options => { options.CredentialProvider = new ConfigurationCredentialProvider(Configuration); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseDefaultFiles(); app.UseStaticFiles(); app.UseBotFramework(); } } }
Program 클래스 파일을 열고 해당 파일의 코드가 다음과 같은지 확인합니다.
using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; namespace MyBot { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build(); } }
변경 내용을 저장하려면 Visual Studio 맨 위에 있는 도구 모음에서 파일>모두 저장으로 이동해야 합니다.
2장 - Azure Bot Service 만들기
이제 봇에 대한 코드를 빌드했으므로 Azure Portal의 Web App Bot Service instance 게시해야 합니다. 이 장에서는 Azure에서 Bot Service 만들고 구성한 다음 코드를 게시하는 방법을 보여줍니다.
먼저 Azure Portal(https://portal.azure.com)에 로그인합니다.
- Azure 계정이 아직 없는 경우 계정을 만들어야 합니다. 교실 또는 랩 상황에서 이 자습서를 따르는 경우 강사 또는 감독관에게 새 계정 설정에 대한 도움을 요청하세요.
로그인한 후 왼쪽 위 모서리에서 리소스 만들기를 클릭하고 웹앱 봇을 검색하고 Enter 키를 클릭합니다.
새 페이지에서는 Web App Bot Service에 대한 설명을 제공합니다. 이 페이지의 왼쪽 아래에서 만들기 단추를 선택하여 이 서비스와의 연결을 만듭니다.
만들기를 클릭한 후에는 다음을 수행합니다.
이 서비스 instance 원하는 이름을 삽입합니다.
구독을 선택합니다.
리소스 그룹을 선택하거나 새 리소스 그룹을 만듭니다. 리소스 그룹은 Azure 자산 컬렉션에 대한 액세스를 모니터링, 제어, 프로비전 및 관리하는 방법을 제공합니다. 단일 프로젝트(예: 이러한 과정)와 연결된 모든 Azure 서비스를 공통 리소스 그룹 아래에 유지하는 것이 좋습니다.
Azure 리소스 그룹에 대해 자세히 알아보려면 이 링크를 따르세요.
리소스 그룹의 위치를 결정합니다(새 리소스 그룹을 만드는 경우). 위치는 애플리케이션이 실행되는 지역에 있는 것이 가장 좋습니다. 일부 Azure 자산은 특정 지역에서만 사용할 수 있습니다.
적합한 가격 책정 계층 을 선택합니다. Web App Bot Service를 처음 만드는 경우 무료 계층(F0이라는 이름)을 사용할 수 있어야 합니다.
앱 이름은봇 이름과 동일하게 남을 수 있습니다.
봇 템플릿을 기본(C#)으로 둡니다.
앱 서비스 계획/위치 가 계정에 대해 자동으로 채워져 있어야 합니다.
봇을 호스트하는 데 사용할 Azure Storage 를 설정합니다. 아직 없는 경우 여기에서 만들 수 있습니다.
또한 본 서비스에 적용된 사용 약관을 이해했다는 것을 확인해야 합니다.
만들기를 클릭합니다.
만들기를 클릭하면 서비스가 생성될 때까지 기다려야 합니다. 이 작업은 1분 정도 걸릴 수 있습니다.
서비스 instance 만들어지면 포털에 알림이 표시됩니다.
알림을 클릭하여 새 서비스 instance 탐색합니다.
알림에서 리소스로 이동 단추를 클릭하여 새 서비스 instance 탐색합니다. 새 Azure 서비스 instance 이동합니다.
이 시점에서 클라이언트 애플리케이션이 이 Bot Service 통신할 수 있도록 Direct Line 라는 기능을 설정해야 합니다. 채널을 클릭한 다음 추천 채널 추가 섹션에서 Direct Line 채널 구성을 클릭합니다.
이 페이지에서는 클라이언트 앱이 봇을 사용하여 인증할 수 있도록 하는 비밀 키를 찾을 수 있습니다. 표시 단추를 클릭하고 프로젝트 뒷부분에서 이 항목이 필요하므로 표시된 키 중 하나의 복사본을 가져옵니다.
3장 - Azure Web App Bot Service 봇 게시
이제 서비스가 준비되었으므로 이전에 빌드한 봇 코드를 새로 만든 웹앱 Bot Service 게시해야 합니다.
참고
봇 솔루션/코드를 변경할 때마다 Azure 서비스에 봇을 게시해야 합니다.
이전에 만든 Visual Studio 솔루션에 돌아가기.
MyBot 프로젝트를 마우스 오른쪽 단추로 클릭하고 솔루션 탐색기게시를 클릭합니다.
게시 대상 선택 페이지에서 App Service 클릭한 다음 기존 항목을 선택하고 마지막으로 프로필 만들기를 클릭합니다(게시 단추와 함께 드롭다운 화살표를 클릭해야 할 수 있습니다.이 항목이 표시되지 않으면).
Microsoft 계정에 아직 로그인하지 않은 경우 여기에서 로그인해야 합니다.
게시 페이지에서 Web App Bot Service 만들기에 사용한 것과 동일한 구독을 설정해야 합니다. 그런 다음, 리소스 그룹으로보기를 설정하고 드롭다운 폴더 구조에서 이전에 만든 리소스 그룹을 선택합니다. 확인을 클릭합니다.
이제 게시 단추를 클릭하고 봇이 게시될 때까지 기다립니다(몇 분 정도 걸릴 수 있음).
4장 - Unity 프로젝트 설정
다음은 혼합 현실로 개발하기 위한 일반적인 설정이며, 따라서 다른 프로젝트에 적합한 템플릿입니다.
Unity 열고 새로 만들기를 클릭합니다.
이제 Unity 프로젝트 이름을 제공해야 합니다. HoloLens 봇을 삽입합니다. 프로젝트 템플릿이 3D로 설정되어 있는지 확인합니다. 위치를 적절한 위치로 설정합니다(루트 디렉터리에 더 가까울수록 좋습니다). 그런 다음 프로젝트 만들기를 클릭합니다.
Unity 열려 있으면 기본 스크립트 편집기Visual Studio로 설정되어 있는지 확인할 가치가 있습니다. 기본 설정 편집 > 으로 이동한 다음 새 창에서 외부 도구로 이동합니다. 외부 스크립트 편집기Visual Studio 2017로 변경합니다. 기본 설정 창을 닫습니다.
다음으로 파일 > 빌드 설정으로 이동하여 유니버설 Windows 플랫폼 선택한 다음 플랫폼 전환 단추를 클릭하여 선택을 적용합니다.
파일 > 빌드 설정에 있는 동안 다음을 확인합니다.
대상 디바이스 가 HoloLens로 설정되었습니다.
몰입형 헤드셋의 경우 대상 디바이스 를 모든 디바이스로 설정합니다.
빌드 유형이 D3D로 설정됨
SDK가 최신 설치됨으로 설정됨
Visual Studio 버전 이 설치된 최신 버전으로 설정됨
빌드 및 실행이 로컬 컴퓨터로 설정됩니다.
장면을 저장하고 빌드에 추가합니다.
열린 장면 추가를 선택하여 이 작업을 수행합니다. 저장 창이 나타납니다.
이에 대한 새 폴더 및 이후의 장면을 만든 다음 새 폴더 단추를 선택하여 새 폴더 를 만들고 이름을 Scenes로 지정 합니다.
새로 만든 Scenes 폴더를 연 다음 파일 이름: 텍스트 필드에 BotScene을 입력한 다음 저장을 클릭합니다.
빌드 설정의 나머지 설정은 현재 기본값으로 남아 있어야 합니다.
빌드 설정 창에서 플레이어 설정 단추를 클릭하면 Inspector가 있는 공간에서 관련 패널이 열립니다.
이 패널에서 몇 가지 설정을 확인해야 합니다.
기타 설정 탭에서 다음을 수행합니다.
런타임 버전 스크립팅은 실험적이어야 합니다(NET 4.6 등가). 이를 변경하려면 편집기 다시 시작해야 합니다.
스크립팅 백 엔드는 .NET이어야 합니다.
API 호환성 수준은.NET 4.6이어야 합니다.
게시 설정 탭의 기능에서 다음을 검사.
InternetClient
마이크
패널 아래에서 XR 설정(게시 설정 아래에 있음)에서 Virtual Reality Supported를 체크하고 Windows Mixed Reality SDK가 추가되었는지 확인합니다.
빌드 설정으로 돌아갑니다Unity C# 프로젝트가 더 이상 회색으로 표시되지 않습니다. 이 옆에 있는 확인란을 선택합니다.
빌드 설정 창을 닫습니다.
장면 및 프로젝트 저장(FILE > SAVE SCENE / FILE > SAVE PROJECT).
5장 - 카메라 설정
중요
이 과정의 Unity 구성 요소 설정을 건너뛰고 코드를 계속 진행하려면 이 Azure-MR-312-Package.unitypackage를 자유롭게 다운로드하고 사용자 지정 패키지로 프로젝트로 가져온 다음 7장에서 계속 진행하세요.
계층 구조 패널에서 기본 카메라를 선택합니다.
선택한 후에는 검사기 패널에서 주 카메라의 모든 구성 요소를 볼 수 있습니다.
- 카메라 개체의 이름은 Main Camera여야 합니다(맞춤법 유의).
- Main Camera 태그 는 MainCamera 로 설정해야 합니다(맞춤법 참고).
- 변환 위치가 0, 0, 0으로 설정되어 있는지 확인합니다.
- 플래그 지우기를 단색으로 설정합니다.
- 카메라 구성 요소의 배경색을 검은색, 알파 0(16진수 코드: #00000000)으로 설정합니다.
6장 - Newtonsoft 라이브러리 가져오기
수신되어 Bot Service 전송된 개체를 역직렬화하고 직렬화하려면 Newtonsoft 라이브러리를 다운로드해야 합니다. 여기에서 올바른 Unity 폴더 구조로 이미 구성된 호환되는 버전을 찾을 수 있습니다.
Newtonsoft 라이브러리를 프로젝트로 가져오려면 이 과정과 함께 제공된 Unity 패키지를 사용합니다.
자산 패키지사용자 지정 패키지가져오기> 메뉴 옵션을 사용하여 .unitypackage를 Unity 추가합니다>.
표시되는 Unity 패키지 가져오기 상자에서 플러그 인(및 포함)의 모든 항목이 선택되어 있는지 확인합니다.
가져오기 단추를 클릭하여 프로젝트에 항목을 추가합니다.
프로젝트 보기의 플러그 인 아래에 있는 Newtonsoft 폴더로 이동하여 Newtonsoft 플러그 인을 선택합니다.
Newtonsoft 플러그 인을 선택한 상태에서 모든 플랫폼 이 선택 취소되었는지 확인하고 WSAPlayer 도 선택 취소되었는지 확인하고 적용을 클릭합니다. 이는 파일이 올바르게 구성되었는지 확인하기 위한 것입니다.
참고
이러한 플러그 인을 표시하면 Unity 편집기만 사용하도록 구성됩니다. WSA 폴더에는 프로젝트를 Unity 내보낸 후에 사용할 다른 집합이 있습니다.
다음으로 Newtonsoft 폴더 내에서 WSA 폴더를 열어야 합니다. 방금 구성한 것과 동일한 파일의 복사본이 표시됩니다. 파일을 선택한 다음, 검사기에서
- 모든 플랫폼 이 선택 취소됨
- WSAPlayer만확인됨
- Dont process 가 선택됨
7장 - BotTag 만들기
BotTag라는 새 Tag 개체를 만듭니다. 장면에서 기본 카메라를 선택합니다. 검사기 패널에서 태그 드롭다운 메뉴를 클릭합니다. 태그 추가를 클릭합니다.
기호를 + 클릭합니다. 새 태그 이름을 BotTag, Save로 지정합니다.
경고
BotTag를 주 카메라에 적용하지 마세요. 실수로 이 작업을 수행한 경우 Main Camera 태그를 MainCamera로 다시 변경해야 합니다.
8장 – BotObjects 클래스 만들기
만들어야 하는 첫 번째 스크립트는 BotObjects 클래스입니다. 이 클래스는 일련의 다른 클래스 개체를 동일한 스크립트 내에 저장하고 장면의 다른 스크립트에서 액세스할 수 있도록 만든 빈 클래스입니다.
이 클래스의 생성은 단지 아키텍처 선택이며, 이러한 개체는 이 과정의 뒷부분에서 만들 Bot 스크립트에서 호스트될 수 있습니다.
이 클래스를 만들려면 다음을 수행합니다.
프로젝트 패널을 마우스 오른쪽 단추로 클릭한 다음 폴더 만들기>를 클릭합니다. 폴더 이름을 스크립트로 지정합니다.
Scripts 폴더를 두 번 클릭하여 엽니다. 그런 다음 해당 폴더 내에서 마우스 오른쪽 단추를 클릭하고 C# 스크립트 만들기 > 를 선택합니다. 스크립트 이름을 BotObjects로 지정합니다.
새 BotObjects 스크립트를 두 번 클릭하여 Visual Studio에서 엽니다.
스크립트의 내용을 삭제하고 다음 코드로 바꿉다.
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class BotObjects : MonoBehaviour{} /// <summary> /// Object received when first opening a conversation /// </summary> [Serializable] public class ConversationObject { public string ConversationId; public string token; public string expires_in; public string streamUrl; public string referenceGrammarId; } /// <summary> /// Object including all Activities /// </summary> [Serializable] public class ActivitiesRootObject { public List<Activity> activities { get; set; } public string watermark { get; set; } } [Serializable] public class Conversation { public string id { get; set; } } [Serializable] public class From { public string id { get; set; } public string name { get; set; } } [Serializable] public class Activity { public string type { get; set; } public string channelId { get; set; } public Conversation conversation { get; set; } public string id { get; set; } public From from { get; set; } public string text { get; set; } public string textFormat { get; set; } public DateTime timestamp { get; set; } public string serviceUrl { get; set; } }
Unity 돌아가기 전에 Visual Studio에서 변경 내용을 저장해야 합니다.
9장 – GazeInput 클래스 만들기
만들려는 다음 클래스는 GazeInput 클래스입니다. 이 클래스는 다음을 담당합니다.
- 플레이어의 응시 를 나타내는 커서를 만듭니다.
- 플레이어의 응시에 의해 적중된 개체를 감지하고 감지된 개체에 대한 참조를 보유합니다.
이 클래스를 만들려면 다음을 수행합니다.
이전에 만든 Scripts 폴더로 이동합니다.
C# 스크립트 만들기 폴더 내부를 마우스 오른쪽 단추로 >클릭합니다. GazeInput 스크립트를 호출합니다.
새 GazeInput 스크립트를 두 번 클릭하여 Visual Studio에서 엽니다.
클래스 이름 맨 위에 다음 줄을 삽입합니다.
/// <summary> /// Class responsible for the User's gaze interactions /// </summary> [System.Serializable] public class GazeInput : MonoBehaviour
그런 다음 Start() 메서드 위에 GazeInput 클래스 내에 다음 변수를 추가합니다.
[Tooltip("Used to compare whether an object is to be interacted with.")] internal string InteractibleTag = "BotTag"; /// <summary> /// Length of the gaze /// </summary> internal float GazeMaxDistance = 300; /// <summary> /// Object currently gazed /// </summary> internal GameObject FocusedObject { get; private set; } internal GameObject _oldFocusedObject { get; private set; } internal RaycastHit HitInfo { get; private set; } /// <summary> /// Cursor object visible in the scene /// </summary> internal GameObject Cursor { get; private set; } internal bool Hit { get; private set; } internal Vector3 Position { get; private set; } internal Vector3 Normal { get; private set; } private Vector3 _gazeOrigin; private Vector3 _gazeDirection;
Start() 메서드에 대한 코드를 추가해야 합니다. 클래스가 초기화될 때 호출됩니다.
/// <summary> /// Start method used upon initialization. /// </summary> internal virtual void Start() { FocusedObject = null; Cursor = CreateCursor(); }
응시 커서를 인스턴스화하고 설정하는 메서드를 구현합니다.
/// <summary> /// Method to create a cursor object. /// </summary> internal GameObject CreateCursor() { GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere); newCursor.SetActive(false); // Remove the collider, so it does not block Raycast. Destroy(newCursor.GetComponent<SphereCollider>()); newCursor.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f); Material mat = new Material(Shader.Find("Diffuse")); newCursor.GetComponent<MeshRenderer>().material = mat; mat.color = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f); newCursor.SetActive(true); return newCursor; }
주 카메라에서 Raycast를 설치하고 현재 포커스가 있는 개체를 추적하는 메서드를 구현합니다.
/// <summary> /// Called every frame /// </summary> internal virtual void Update() { _gazeOrigin = Camera.main.transform.position; _gazeDirection = Camera.main.transform.forward; UpdateRaycast(); } /// <summary> /// Reset the old focused object, stop the gaze timer, and send data if it /// is greater than one. /// </summary> private void ResetFocusedObject() { // Ensure the old focused object is not null. if (_oldFocusedObject != null) { if (_oldFocusedObject.CompareTag(InteractibleTag)) { // Provide the OnGazeExited event. _oldFocusedObject.SendMessage("OnGazeExited", SendMessageOptions.DontRequireReceiver); } } } private void UpdateRaycast() { // Set the old focused gameobject. _oldFocusedObject = FocusedObject; RaycastHit hitInfo; // Initialize Raycasting. Hit = Physics.Raycast(_gazeOrigin, _gazeDirection, out hitInfo, GazeMaxDistance); HitInfo = hitInfo; // Check whether raycast has hit. if (Hit == true) { Position = hitInfo.point; Normal = hitInfo.normal; // Check whether the hit has a collider. if (hitInfo.collider != null) { // Set the focused object with what the user just looked at. FocusedObject = hitInfo.collider.gameObject; } else { // Object looked on is not valid, set focused gameobject to null. FocusedObject = null; } } else { // No object looked upon, set focused gameobject to null. FocusedObject = null; // Provide default position for cursor. Position = _gazeOrigin + (_gazeDirection * GazeMaxDistance); // Provide a default normal. Normal = _gazeDirection; } // Lerp the cursor to the given position, which helps to stabilize the gaze. Cursor.transform.position = Vector3.Lerp(Cursor.transform.position, Position, 0.6f); // Check whether the previous focused object is this same. If so, reset the focused object. if (FocusedObject != _oldFocusedObject) { ResetFocusedObject(); if (FocusedObject != null) { if (FocusedObject.CompareTag(InteractibleTag)) { // Provide the OnGazeEntered event. FocusedObject.SendMessage("OnGazeEntered", SendMessageOptions.DontRequireReceiver); } } } }
Unity 돌아가기 전에 Visual Studio에서 변경 내용을 저장해야 합니다.
10장 – 봇 클래스 만들기
지금 만들 스크립트를 봇이라고 합니다. 이 클래스는 애플리케이션의 핵심 클래스이며 다음을 저장합니다.
- 웹앱 봇 자격 증명
- 사용자 음성 명령을 수집하는 메서드
- 웹앱 봇과의 대화를 시작하는 데 필요한 메서드
- 웹앱 봇에 메시지를 보내는 데 필요한 메서드
Bot Service 메시지를 보내기 위해 SendMessageToBot() 코루틴은 사용자가 보낸 데이터로 Bot Framework에서 인식하는 개체인 활동을 빌드합니다.
이 클래스를 만들려면 다음을 수행합니다.
스크립트 폴더를 두 번 클릭하여 엽니다.
Scripts 폴더 내부를 마우스 오른쪽 단추로 클릭하고 C# 스크립트 만들기>를 클릭합니다. 스크립트 이름을 봇으로 지정 합니다.
새 스크립트를 두 번 클릭하여 Visual Studio에서 엽니다.
Bot 클래스의 맨 위에 있는 네임스페이스를 다음과 동일하게 업데이트합니다.
using Newtonsoft.Json; using System.Collections; using System.Text; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Windows.Speech;
Bot 클래스 내에 다음 변수를 추가합니다.
/// <summary> /// Static instance of this class /// </summary> public static Bot Instance; /// <summary> /// Material of the sphere representing the Bot in the scene /// </summary> internal Material botMaterial; /// <summary> /// Speech recognizer class reference, which will convert speech to text. /// </summary> private DictationRecognizer dictationRecognizer; /// <summary> /// Use this variable to identify the Bot Id /// Can be any value /// </summary> private string botId = "MRBotId"; /// <summary> /// Use this variable to identify the Bot Name /// Can be any value /// </summary> private string botName = "MRBotName"; /// <summary> /// The Bot Secret key found on the Web App Bot Service on the Azure Portal /// </summary> private string botSecret = "-- Add your Secret Key here --"; /// <summary> /// Bot Endpoint, v4 Framework uses v3 endpoint at this point in time /// </summary> private string botEndpoint = "https://directline.botframework.com/v3/directline"; /// <summary> /// The conversation object reference /// </summary> private ConversationObject conversation; /// <summary> /// Bot states to regulate the application flow /// </summary> internal enum BotState {ReadyToListen, Listening, Processing} /// <summary> /// Flag for the Bot state /// </summary> internal BotState botState; /// <summary> /// Flag for the conversation status /// </summary> internal bool conversationStarted = false;
참고
Bot Secret Key를 botSecret 변수에 삽입해야 합니다. 이 과정의 시작 부분에서 2장, 10단계에서 봇 비밀 키를 적어 두게 됩니다.
이제 Awake() 및 Start()에 대한 코드를 추가해야 합니다.
/// <summary> /// Called on Initialization /// </summary> void Awake() { Instance = this; } /// <summary> /// Called immediately after Awake method /// </summary> void Start() { botState = BotState.ReadyToListen; }
음성 캡처가 시작되고 끝날 때 음성 라이브러리에서 호출되는 두 처리기를 추가합니다. DictationRecognizer는 사용자가 말을 중지하면 사용자 음성 캡처를 자동으로 중지합니다.
/// <summary> /// Start microphone capture. /// </summary> public void StartCapturingAudio() { botState = BotState.Listening; botMaterial.color = Color.red; // Start dictation dictationRecognizer = new DictationRecognizer(); dictationRecognizer.DictationResult += DictationRecognizer_DictationResult; dictationRecognizer.Start(); } /// <summary> /// Stop microphone capture. /// </summary> public void StopCapturingAudio() { botState = BotState.Processing; dictationRecognizer.Stop(); }
다음 처리기는 사용자 음성 입력의 결과를 수집하고 웹앱 Bot Service 메시지를 보내는 코루틴을 호출합니다.
/// <summary> /// This handler is called every time the Dictation detects a pause in the speech. /// </summary> private void DictationRecognizer_DictationResult(string text, ConfidenceLevel confidence) { // Update UI with dictation captured Debug.Log($"User just said: {text}"); // Send dictation to Bot StartCoroutine(SendMessageToBot(text, botId, botName, "message")); StopCapturingAudio(); }
다음 코루틴은 봇과의 대화를 시작하기 위해 호출됩니다. 대화 호출이 완료되면 Bot Service 보낼 작업을 빈 메시지로 설정하는 일련의 매개 변수를 전달하여 SendMessageToCoroutine()을 호출합니다. 이는 Bot Service 대화를 시작하라는 메시지를 표시하기 위해 수행됩니다.
/// <summary> /// Request a conversation with the Bot Service /// </summary> internal IEnumerator StartConversation() { string conversationEndpoint = string.Format("{0}/conversations", botEndpoint); WWWForm webForm = new WWWForm(); using (UnityWebRequest unityWebRequest = UnityWebRequest.Post(conversationEndpoint, webForm)) { unityWebRequest.SetRequestHeader("Authorization", "Bearer " + botSecret); unityWebRequest.downloadHandler = new DownloadHandlerBuffer(); yield return unityWebRequest.SendWebRequest(); string jsonResponse = unityWebRequest.downloadHandler.text; conversation = new ConversationObject(); conversation = JsonConvert.DeserializeObject<ConversationObject>(jsonResponse); Debug.Log($"Start Conversation - Id: {conversation.ConversationId}"); conversationStarted = true; } // The following call is necessary to create and inject an activity of type //"conversationUpdate" to request a first "introduction" from the Bot Service. StartCoroutine(SendMessageToBot("", botId, botName, "conversationUpdate")); }
다음 코루틴은 Bot Service 보낼 활동을 빌드하기 위해 호출됩니다.
/// <summary> /// Send the user message to the Bot Service in form of activity /// and call for a response /// </summary> private IEnumerator SendMessageToBot(string message, string fromId, string fromName, string activityType) { Debug.Log($"SendMessageCoroutine: {conversation.ConversationId}, message: {message} from Id: {fromId} from name: {fromName}"); // Create a new activity here Activity activity = new Activity(); activity.from = new From(); activity.conversation = new Conversation(); activity.from.id = fromId; activity.from.name = fromName; activity.text = message; activity.type = activityType; activity.channelId = "DirectLineChannelId"; activity.conversation.id = conversation.ConversationId; // Serialize the activity string json = JsonConvert.SerializeObject(activity); string sendActivityEndpoint = string.Format("{0}/conversations/{1}/activities", botEndpoint, conversation.ConversationId); // Send the activity to the Bot using (UnityWebRequest www = new UnityWebRequest(sendActivityEndpoint, "POST")) { www.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json)); www.downloadHandler = new DownloadHandlerBuffer(); www.SetRequestHeader("Authorization", "Bearer " + botSecret); www.SetRequestHeader("Content-Type", "application/json"); yield return www.SendWebRequest(); // extrapolate the response Id used to keep track of the conversation string jsonResponse = www.downloadHandler.text; string cleanedJsonResponse = jsonResponse.Replace("\r\n", string.Empty); string responseConvId = cleanedJsonResponse.Substring(10, 30); // Request a response from the Bot Service StartCoroutine(GetResponseFromBot(activity)); } }
다음 코루틴은 Bot Service 활동을 보낸 후 응답을 요청하기 위해 호출됩니다.
/// <summary> /// Request a response from the Bot by using a previously sent activity /// </summary> private IEnumerator GetResponseFromBot(Activity activity) { string getActivityEndpoint = string.Format("{0}/conversations/{1}/activities", botEndpoint, conversation.ConversationId); using (UnityWebRequest unityWebRequest1 = UnityWebRequest.Get(getActivityEndpoint)) { unityWebRequest1.downloadHandler = new DownloadHandlerBuffer(); unityWebRequest1.SetRequestHeader("Authorization", "Bearer " + botSecret); yield return unityWebRequest1.SendWebRequest(); string jsonResponse = unityWebRequest1.downloadHandler.text; ActivitiesRootObject root = new ActivitiesRootObject(); root = JsonConvert.DeserializeObject<ActivitiesRootObject>(jsonResponse); foreach (var act in root.activities) { Debug.Log($"Bot Response: {act.text}"); SetBotResponseText(act.text); } botState = BotState.ReadyToListen; botMaterial.color = Color.blue; } }
이 클래스에 추가할 마지막 메서드는 장면에 메시지를 표시하는 데 필요합니다.
/// <summary> /// Set the UI Response Text of the bot /// </summary> internal void SetBotResponseText(string responseString) { SceneOrganiser.Instance.botResponseText.text = responseString; }
참고
Unity 편집기 콘솔 내에서 SceneOrganiser 클래스 누락에 대한 오류가 표시 될 수 있습니다. 자습서의 뒷부분에서 이 클래스를 만들 예정이므로 이 메시지를 무시합니다.
Unity 돌아가기 전에 Visual Studio에서 변경 내용을 저장해야 합니다.
11장 - Interactions 클래스 만들기
지금 만들려는 클래스를 상호 작용이라고 합니다. 이 클래스는 사용자의 HoloLens 탭 입력을 검색하는 데 사용됩니다.
사용자가 장면에서 Bot 개체를 보는 동안 탭하고 봇이 음성 입력을 수신 대기할 준비가 되면 Bot 개체는 색을 빨간색 으로 변경하고 음성 입력을 수신 대기하기 시작합니다.
이 클래스는 GazeInput 클래스에서 상속되므로 Base를 사용하여 나타내는 해당 클래스의 Start() 메서드 및 변수를 참조할 수 있습니다.
이 클래스를 만들려면 다음을 수행합니다.
스크립트 폴더를 두 번 클릭하여 엽니다.
Scripts 폴더 내부를 마우스 오른쪽 단추로 클릭하고 C# 스크립트 만들기>를 클릭합니다. 스크립트 이름을 상호 작용으로 지정합니다.
새 스크립트를 두 번 클릭하여 Visual Studio에서 엽니다.
Interactions 클래스의 맨 위에 있는 네임스페이스 및 클래스 상속을 다음과 동일하게 업데이트합니다.
using UnityEngine.XR.WSA.Input; public class Interactions : GazeInput {
Interactions 클래스 내에 다음 변수가 추가됩니다.
/// <summary> /// Allows input recognition with the HoloLens /// </summary> private GestureRecognizer _gestureRecognizer;
그런 다음 Start() 메서드를 추가합니다.
/// <summary> /// Called on initialization, after Awake /// </summary> internal override void Start() { base.Start(); //Register the application to recognize HoloLens user inputs _gestureRecognizer = new GestureRecognizer(); _gestureRecognizer.SetRecognizableGestures(GestureSettings.Tap); _gestureRecognizer.Tapped += GestureRecognizer_Tapped; _gestureRecognizer.StartCapturingGestures(); }
사용자가 HoloLens 카메라 앞에서 탭 제스처를 수행할 때 트리거되는 처리기를 추가합니다.
/// <summary> /// Detects the User Tap Input /// </summary> private void GestureRecognizer_Tapped(TappedEventArgs obj) { // Ensure the bot is being gazed upon. if(base.FocusedObject != null) { // If the user is tapping on Bot and the Bot is ready to listen if (base.FocusedObject.name == "Bot" && Bot.Instance.botState == Bot.BotState.ReadyToListen) { // If a conversation has not started yet, request one if(Bot.Instance.conversationStarted) { Bot.Instance.SetBotResponseText("Listening..."); Bot.Instance.StartCapturingAudio(); } else { Bot.Instance.SetBotResponseText("Requesting Conversation..."); StartCoroutine(Bot.Instance.StartConversation()); } } } }
Unity 돌아가기 전에 Visual Studio에서 변경 내용을 저장해야 합니다.
12장 – SceneOrganiser 클래스 만들기
이 랩에 필요한 마지막 클래스를 SceneOrganiser라고 합니다. 이 클래스는 기본 카메라에 구성 요소 및 스크립트를 추가하고 장면에 적절한 개체를 만들어 장면을 프로그래밍 방식으로 설정합니다.
이 클래스를 만들려면 다음을 수행합니다.
스크립트 폴더를 두 번 클릭하여 엽니다.
Scripts 폴더 내부를 마우스 오른쪽 단추로 클릭하고 C# 스크립트 만들기>를 클릭합니다. 스크립트 이름을 SceneOrganiser로 지정합니다.
새 스크립트를 두 번 클릭하여 Visual Studio에서 엽니다.
SceneOrganiser 클래스 내에 다음 변수를 추가합니다.
/// <summary> /// Static instance of this class /// </summary> public static SceneOrganiser Instance; /// <summary> /// The 3D text representing the Bot response /// </summary> internal TextMesh botResponseText;
그런 다음 , Awake() 및 Start() 메서드를 추가합니다.
/// <summary> /// Called on Initialization /// </summary> private void Awake() { Instance = this; } /// <summary> /// Called immediately after Awake method /// </summary> void Start () { // Add the GazeInput class to this object gameObject.AddComponent<GazeInput>(); // Add the Interactions class to this object gameObject.AddComponent<Interactions>(); // Create the Bot in the scene CreateBotInScene(); }
장면에서 Bot 개체를 만들고 매개 변수 및 구성 요소를 설정하는 다음 메서드를 추가합니다.
/// <summary> /// Create the Sign In button object in the scene /// and sets its properties /// </summary> private void CreateBotInScene() { GameObject botObjInScene = GameObject.CreatePrimitive(PrimitiveType.Sphere); botObjInScene.name = "Bot"; // Add the Bot class to the Bot GameObject botObjInScene.AddComponent<Bot>(); // Create the Bot UI botResponseText = CreateBotResponseText(); // Set properties of Bot GameObject Bot.Instance.botMaterial = new Material(Shader.Find("Diffuse")); botObjInScene.GetComponent<Renderer>().material = Bot.Instance.botMaterial; Bot.Instance.botMaterial.color = Color.blue; botObjInScene.transform.position = new Vector3(0f, 2f, 10f); botObjInScene.tag = "BotTag"; }
봇의 응답을 나타내는 장면에서 UI 개체를 만드는 데 책임이 있는 다음 메서드를 추가합니다.
/// <summary> /// Spawns cursor for the Main Camera /// </summary> private TextMesh CreateBotResponseText() { // Create a sphere as new cursor GameObject textObject = new GameObject(); textObject.transform.parent = Bot.Instance.transform; textObject.transform.localPosition = new Vector3(0,1,0); // Resize the new cursor textObject.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f); // Creating the text of the Label TextMesh textMesh = textObject.AddComponent<TextMesh>(); textMesh.anchor = TextAnchor.MiddleCenter; textMesh.alignment = TextAlignment.Center; textMesh.fontSize = 50; textMesh.text = "Hi there, tap on me and I will start listening."; return textMesh; }
Unity 돌아가기 전에 Visual Studio에서 변경 내용을 저장해야 합니다.
Unity 편집기 Scripts 폴더에서 Main Camera로 SceneOrganiser 스크립트를 끕니다. 이제 아래 이미지와 같이 장면 주최자 구성 요소가 Main Camera 개체에 표시됩니다.
챕터 13 – 빌드 전
애플리케이션에 대한 철저한 테스트를 수행하려면 HoloLens에 테스트용으로 로드해야 합니다. 이렇게 하기 전에 다음을 확인합니다.
- 4장에 언급된 모든 설정이 올바르게 설정됩니다.
- Script SceneOrganiser는 Main Camera 개체에 연결됩니다.
- Bot 클래스에서 botSecret 변수에 Bot Secret Key를 삽입했는지 확인합니다.
챕터 14 – HoloLens에 빌드 및 테스트용 로드
이 프로젝트의 Unity 섹션에 필요한 모든 작업이 완료되었으므로 이제 Unity 빌드해야 합니다.
빌드 설정, 파일 > 빌드 설정...으로 이동합니다.
빌드 설정 창에서 빌드를 클릭합니다.
아직 없는 경우 C# 프로젝트 Unity 선택합니다.
빌드를 클릭합니다. Unity 만들어야 하는 파일 탐색기 창을 시작하고 앱을 빌드할 폴더를 선택합니다. 이제 해당 폴더를 만들고 이름을 앱으로 지정 합니다. 그런 다음 , App 폴더를 선택한 상태에서 폴더 선택을 클릭합니다.
Unity App 폴더에 프로젝트를 빌드하기 시작합니다.
Unity 빌드가 완료되면(시간이 좀 걸릴 수 있음) 빌드 위치에 파일 탐색기 창이 열립니다(작업 표시줄을 검사 항상 창 위에 표시되지는 않지만 새 창이 추가되었음을 알려 줍니다).
15장 - HoloLens에 배포
HoloLens에 배포하려면 다음을 수행합니다.
HoloLens의 IP 주소(원격 배포의 경우)가 필요하며 HoloLens가 개발자 모드에 있는지 확인해야 합니다. 이렇게 하려면 다음을 수행합니다.
- HoloLens를 착용하는 동안 설정을 엽니다.
- 네트워크 & 인터넷 > Wi-Fi > 고급 옵션으로 이동합니다.
- IPv4 주소를 기록해 둡니다.
- 다음으로 설정으로 다시 이동한 다음 개발자를 위한 & 보안을 > 업데이트합니다.
- 개발자 모드를 설정합니다.
새 Unity 빌드(App 폴더)로 이동하여 Visual Studio를 사용하여 솔루션 파일을 엽니다.
솔루션 구성에서 디버그를 선택합니다.
솔루션 플랫폼에서x86, 원격 머신을 선택합니다.
빌드 메뉴로 이동하여 솔루션 배포를 클릭하여 HoloLens에 애플리케이션을 테스트용으로 로드합니다.
이제 시작 준비가 된 HoloLens에 설치된 앱 목록에 앱이 표시됩니다.
참고
몰입형 헤드셋에 배포하려면 솔루션 플랫폼을로컬 머신으로 설정하고 구성을 디버그로 설정하고x86을 플랫폼으로 설정합니다. 그런 다음 빌드 메뉴를 사용하여 로컬 컴퓨터에 배포하고 솔루션 배포를 선택합니다.
16장 – HoloLens에서 애플리케이션 사용
애플리케이션을 시작하면 봇이 앞에서 파란색 구로 표시됩니다.
구를 응시하는 동안 탭 제스처 를 사용하여 대화를 시작합니다.
대화가 시작될 때까지 기다립니다(UI는 메시지가 표시될 때 표시됨). 봇에서 소개 메시지를 받으면 봇을 다시 탭하여 빨간색으로 변하고 음성을 듣기 시작합니다.
통신을 중지하면 애플리케이션이 봇에 메시지를 보내고 UI에 표시되는 응답을 즉시 받게 됩니다.
프로세스를 반복하여 봇에 더 많은 메시지를 보냅니다(메시지를 감지할 때마다 탭해야 합니다).
이 대화에서는 봇이 정보(사용자 이름)를 유지하는 동시에 알려진 정보(예: 재고가 있는 항목)를 제공하는 방법을 보여 줍니다.
봇에 질문할 몇 가지 질문:
what do you sell?
how much are umbrellas?
how much are raincoats?
완료된 웹앱 봇(v4) 애플리케이션
축하합니다. v4를 Microsoft Bot Framework Azure Web App Bot을 활용하는 혼합 현실 앱을 빌드했습니다.
보너스 연습
연습 1
이 랩의 대화 구조는 매우 기본입니다. Microsoft LUIS를 사용하여 봇 자연어 이해 기능을 제공합니다.
연습 2
이 예제에는 대화를 종료하고 새 대화를 다시 시작하는 것은 포함되지 않습니다. 봇 기능을 완료하려면 대화에 대한 닫기를 구현해 보세요.