HoloLens (1.ª geração) e Azure 312: integração de Bot
Nota
Os tutoriais da Academia de Realidade Mista foram projetados com HoloLens (1ª geração) e Headsets Imersivos de Realidade Mista em mente. Como tal, sentimos que é importante deixar estes tutoriais no lugar para desenvolvedores que ainda estão procurando orientação no desenvolvimento para esses dispositivos. Esses tutoriais não serão atualizados com os conjuntos de ferramentas ou interações mais recentes que estão sendo usados para o HoloLens 2. Eles serão mantidos para continuar trabalhando nos dispositivos suportados. Haverá uma nova série de tutoriais que serão publicados no futuro que demonstrarão como desenvolver para o HoloLens 2. Este aviso será atualizado com um link para esses tutoriais quando eles forem publicados.
Neste curso, você aprenderá como criar e implantar um bot usando o Microsoft Bot Framework V4 e se comunicar com ele por meio de um aplicativo Windows Mixed Reality.
O Microsoft Bot Framework V4 é um conjunto de APIs projetado para fornecer aos desenvolvedores as ferramentas para criar um aplicativo bot extensível e escalável. Para obter mais informações, visite a página Microsoft Bot Framework ou o V4 Git Repository.
Depois de concluir este curso, você terá criado um aplicativo Windows Mixed Reality, que será capaz de fazer o seguinte:
- Use um gesto de toque para iniciar o bot ouvindo a voz dos usuários.
- Quando o usuário tiver dito algo, o bot tentará fornecer uma resposta.
- Exiba a resposta dos bots como texto, posicionada perto do bot, na cena Unity.
Na sua aplicação, cabe-lhe a si decidir como irá integrar os resultados com o seu design. Este curso foi criado para ensiná-lo a integrar um Serviço do Azure ao seu projeto Unity. É seu trabalho usar o conhecimento que você ganha com este curso para melhorar sua aplicação de realidade mista.
Suporte de dispositivos
Curso | HoloLens | Auriculares imersivos |
---|---|---|
MR e Azure 312: Integração de Bot | ✔️ | ✔️ |
Nota
Embora este curso se concentre principalmente no HoloLens, você também pode aplicar o que aprendeu neste curso aos fones de ouvido imersivos (VR) do Windows Mixed Reality. Como os fones de ouvido imersivos (VR) não têm câmeras acessíveis, você precisará de uma câmera externa conectada ao seu PC. Ao acompanhar o curso, você verá anotações sobre quaisquer alterações que talvez precise empregar para suportar fones de ouvido imersivos (VR).
Pré-requisitos
Nota
Este tutorial foi projetado para desenvolvedores que têm experiência básica com Unity e C#. Tenha também em atenção que os pré-requisitos e as instruções escritas contidas neste documento representam o que foi testado e verificado no momento da redação (julho de 2018). Você é livre para usar o software mais recente, conforme listado no artigo instalar as ferramentas , embora não se deva presumir que as informações neste curso corresponderão perfeitamente ao que você encontrará em software mais recente do que o listado abaixo.
Recomendamos o seguinte hardware e software para este curso:
- Um PC de desenvolvimento, compatível com Windows Mixed Reality para desenvolvimento imersivo (VR) de auriculares
- Windows 10 Fall Creators Update (ou posterior) com o modo de desenvolvedor ativado
- O SDK mais recente do Windows 10
- Unidade 2017.4
- Visual Studio 2017
- Um auricular imersivo (VR) Windows Mixed Reality ou Microsoft HoloLens com o modo de programador ativado
- Acesso à Internet para o Azure e para a recuperação do Bot do Azure. Para mais informações, por favor siga este link.
Antes de começar
- Para evitar encontrar problemas ao criar este projeto, é altamente recomendável que você crie o projeto mencionado neste tutorial em uma pasta raiz ou quase raiz (caminhos de pasta longos podem causar problemas em tempo de compilação).
- Configure e teste o seu HoloLens. Se você precisar de suporte para configurar seu HoloLens, visite o artigo de configuração do HoloLens.
- É uma boa ideia executar a calibração e o ajuste do sensor ao começar a desenvolver um novo aplicativo HoloLens (às vezes, pode ajudar a executar essas tarefas para cada usuário).
Para obter ajuda sobre calibração, siga este link para o artigo Calibração HoloLens.
Para obter ajuda sobre o ajuste do sensor, siga este link para o artigo HoloLens Sensor Tuning.
Capítulo 1 – Criar o aplicativo Bot
A primeira etapa é criar seu bot como um aplicativo Web ASP.Net Core local. Depois de terminá-lo e testá-lo, você o publicará no Portal do Azure.
Abra o Visual Studio. Crie um novo projeto, selecione ASP NET Core Web Application como o tipo de projeto (você o encontrará na subseção .NET Core) e chame-o de MyBot. Clique em OK.
Na janela que aparecerá, selecione Vazio. Verifique também se o destino está definido como ASP NET Core 2.0 e a Autenticação está definida como Sem Autenticação. Clique em OK.
A solução será agora aberta. Clique com o botão direito do mouse em Solution Mybot no Solution Explorer e clique em Manage NuGet Packages for Solution.
Na guia Procurar, procure Microsoft.Bot.Builder.Integration.AspNet.Core (verifique se a opção Incluir pré-lançamento está marcada). Selecione a versão do pacote 4.0.1-preview, e marque as caixas do projeto. Em seguida, clique em Instalar. Agora você instalou as bibliotecas necessárias para o Bot Framework v4. Feche a página NuGet.
Clique com o botão direito do rato no seu Projeto, MyBot, no Solution Explorer e clique em Add | Class.
Nomeie a classe MyBot e clique em Adicionar.
Repita o ponto anterior para criar outra classe chamada ConversationContext.
Clique com o botão direito do mouse em wwwroot no Gerenciador de Soluções e clique em Adicionar | Novo Item. Selecione Página HTML (você vai encontrá-lo na subseção Web). Nomeie o arquivo default.html. Clique em Adicionar.
A lista de classes / objetos no Gerenciador de Soluções deve se parecer com a imagem abaixo.
Clique duas vezes na classe ConversationContext . Essa classe é responsável por manter as variáveis usadas pelo bot para manter o contexto da conversa. Esses valores de contexto de conversação são mantidos em uma instância dessa classe, porque qualquer instância da classe MyBot será atualizada sempre que uma atividade for recebida. Adicione o seguinte código à classe:
namespace MyBot { public static class ConversationContext { internal static string userName; internal static string userMsg; } }
Clique duas vezes na classe MyBot . Essa classe hospedará os manipuladores chamados por qualquer atividade de entrada do cliente. Nesta classe, você adicionará o código usado para criar a conversa entre o bot e o cliente. Como mencionado anteriormente, uma instância dessa classe é inicializada cada vez que uma atividade é recebida. Adicione o seguinte código a esta classe:
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?"); } } } }
Clique duas vezes na classe Startup . Essa classe inicializará o bot. Adicione o seguinte código à classe:
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(); } } }
Abra o arquivo de classe Program e verifique se o código nele é o mesmo que o seguinte:
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(); } }
Lembre-se de salvar suas alterações, para fazer isso, vá para Arquivo>salvar tudo, na barra de ferramentas na parte superior do Visual Studio.
Capítulo 2 - Criar o Serviço de Bot do Azure
Agora que você criou o código para seu bot, precisa publicá-lo em uma instância do Serviço de Bot de Aplicativo Web, no Portal do Azure. Este capítulo mostrará como criar e configurar o Serviço de Bot no Azure e, em seguida, publicar seu código nele.
Primeiro, faça logon no Portal do Azure (https://portal.azure.com).
- Se ainda não tiver uma conta do Azure, terá de criar uma. Se você estiver seguindo este tutorial em uma situação de sala de aula ou laboratório, peça ajuda ao seu instrutor ou a um dos proctors para configurar sua nova conta.
Depois de fazer login, clique em Criar um recurso no canto superior esquerdo, procure por bot de aplicativo Web e clique em Enter.
A nova página fornecerá uma descrição do Serviço de Bot do Aplicativo Web. No canto inferior esquerdo desta página, selecione o botão Criar para criar uma associação com este Serviço.
Depois de clicar em Criar:
Insira o Nome desejado para esta instância de Serviço.
Selecione uma Subscrição.
Escolha um Grupo de Recursos ou crie um novo. Um grupo de recursos fornece uma maneira de monitorar, controlar o acesso, provisionar e gerenciar a cobrança de uma coleção de ativos do Azure. É recomendável manter todos os Serviços do Azure associados a um único projeto (por exemplo, como esses cursos) em um grupo de recursos comum).
Se desejar ler mais sobre os Grupos de Recursos do Azure, siga este link
Determine a Localização do seu grupo de recursos (se estiver a criar um novo Grupo de Recursos). O ideal seria que o local fosse na região onde o aplicativo seria executado. Alguns ativos do Azure só estão disponíveis em determinadas regiões.
Selecione o Nível de Preço apropriado para você, se esta for a primeira vez que cria um Serviço de Bot de Aplicativo Web, um nível gratuito (chamado F0) deve estar disponível para você
O nome do aplicativo pode ser deixado igual ao nome do Bot.
Deixe o modelo Bot como Basic (C#).
O plano/localização do serviço de aplicativo deve ter sido preenchido automaticamente para sua conta.
Defina o Armazenamento do Azure que você deseja usar para hospedar seu Bot. Se ainda não tiver um, pode criá-lo aqui.
Você também precisará confirmar que compreendeu os Termos e Condições aplicados a este Serviço.
Clique em Criar.
Depois de clicar em Criar, você terá que esperar que o Serviço seja criado, isso pode levar um minuto.
Uma notificação aparecerá no Portal assim que a instância de Serviço for criada.
Clique na notificação para explorar sua nova instância de serviço.
Clique no botão Ir para recurso na notificação para explorar sua nova instância de serviço. Você será levado para sua nova instância do Serviço do Azure.
Neste ponto, você precisa configurar um recurso chamado Linha Direta para permitir que seu aplicativo cliente se comunique com este Serviço de Bot. Clique em Canais e, em seguida, na seção Adicionar um canal em destaque, clique em Configurar canal de Linha Direta.
Nesta página, você encontrará as chaves secretas que permitirão que seu aplicativo cliente se autentique com o bot. Clique no botão Mostrar e tire uma cópia de uma das chaves exibidas, pois você precisará disso mais tarde em seu projeto.
Capítulo 3 – Publicar o Bot no Serviço de Bot do Aplicativo Web do Azure
Agora que seu Serviço está pronto, você precisa publicar seu código de Bot, que você criou anteriormente, em seu Serviço de Bot de Aplicativo Web recém-criado.
Nota
Você terá que publicar seu Bot no Serviço do Azure sempre que fizer alterações na solução/código do Bot.
Volte para sua solução do Visual Studio que você criou anteriormente.
Clique com o botão direito do rato no seu projeto MyBot, no Solution Explorer, e clique em Publicar.
Na página Escolha um destino de publicação, clique em Serviço de Aplicativo, selecione Existente, por último clique em Criar Perfil (talvez seja necessário clicar na seta suspensa ao lado do botão Publicar, se isso não estiver visível).
Se ainda não tiver iniciado sessão na sua Conta Microsoft, tem de o fazer aqui.
Na página Publicar, você verá que precisa definir a mesma Assinatura que usou para a criação do Serviço de Bot de Aplicativo Web. Em seguida, defina a Vista como Grupo de Recursos e, na estrutura de pastas pendentes, selecione o Grupo de Recursos que criou anteriormente. Clique em OK.
Agora clique no botão Publicar e aguarde até que o Bot seja publicado (pode levar alguns minutos).
Capítulo 4 – Criar o projeto Unity
O seguinte é uma configuração típica para desenvolver com realidade mista e, como tal, é um bom modelo para outros projetos.
Abra o Unity e clique em Novo.
Agora você precisará fornecer um nome de projeto Unity. Insira o HoloLens Bot. Verifique se o modelo de projeto está definido como 3D. Defina o Local para algum lugar apropriado para você (lembre-se, mais perto de diretórios raiz é melhor). Em seguida, clique em Criar projeto.
Com o Unity aberto, vale a pena verificar se o Editor de Scripts padrão está definido como Visual Studio. Vá para Editar > Preferências e, na nova janela, navegue até Ferramentas Externas. Altere o Editor de Scripts Externo para Visual Studio 2017. Feche a janela Preferências .
Em seguida, vá para Configurações de compilação de arquivo > e selecione Plataforma Universal do Windows e, em seguida, clique no botão Alternar plataforma para aplicar sua seleção.
Enquanto ainda estiver em Configurações de compilação de arquivo > e certifique-se de que:
O dispositivo alvo está definido como HoloLens
Para os auriculares imersivos, defina Target Device como Any Device.
O tipo de compilação está definido como D3D
O SDK está definido como Instalado mais recente
Versão do Visual Studio está definida como A versão mais recente instalada
Build and Run está definido como Máquina Local
Salve a cena e adicione-a à compilação.
Faça isso selecionando Adicionar cenas abertas. Será exibida uma janela de salvamento.
Crie uma nova pasta para esta e qualquer cena futura e, em seguida, selecione o botão Nova pasta , para criar uma nova pasta, nomeie-a Cenas.
Abra a pasta Cenas recém-criada e, no campo de texto Nome do arquivo:, digite BotScene e clique em Salvar.
As configurações restantes, em Configurações de compilação, devem ser deixadas como padrão por enquanto.
Na janela Configurações de compilação, clique no botão Configurações do player, isso abrirá o painel relacionado no espaço onde o inspetor está localizado.
Neste painel, algumas configurações precisam ser verificadas:
Na guia Outras configurações:
A versão do Scripting Runtime deve ser experimental (equivalente ao NET 4.6), alterando isso exigirá uma reinicialização do Editor.
O back-end de scripts deve ser .NET
O nível de compatibilidade da API deve ser .NET 4.6
Na guia Configurações de publicação , em Recursos, verifique:
InternetClient
Microfone
Mais abaixo no painel, em Configurações XR (encontradas abaixo de Configurações de publicação), marque Realidade Virtual suportada, verifique se o SDK de realidade mista do Windows foi adicionado.
De volta às configurações de compilação, o Unity C# Projects não está mais acinzentado, marque a caixa de seleção ao lado disso.
Feche a janela Configurações de compilação.
Salve sua cena e projeto (FILE > SAVE SCENE / FILE > SAVE PROJECT).
Capítulo 5 – Configuração da câmara
Importante
Se você deseja ignorar o componente Unity set deste curso e continuar direto no código, sinta-se à vontade para baixar este Azure-MR-312-Package.unitypackage, importá-lo para seu projeto como um pacote personalizado e continuar a partir do Capítulo 7.
No painel Hierarquia, selecione a Câmara principal.
Uma vez selecionado, você será capaz de ver todos os componentes da câmera principal no painel Inspetor.
- O objeto Camera deve ser chamado Main Camera (observe a ortografia)
- A tag da câmera principal deve ser definida como MainCamera (observe a ortografia)
- Verifique se a Posição de transformação está definida como 0, 0, 0
- Defina Clear Flags como Solid Color.
- Defina a cor de fundo do componente da câmera para preto, Alpha 0 (código hexadecimal: #00000000)
Capítulo 6 – Importar a biblioteca Newtonsoft
Para ajudá-lo a desserializar e serializar objetos recebidos e enviados para o Serviço de Bot, você precisa baixar a biblioteca Newtonsoft . Você encontrará uma versão compatível já organizada com a estrutura de pastas Unity correta aqui.
Para importar a biblioteca Newtonsoft para o seu projeto, use o Pacote Unity que veio com este curso.
Adicione o .unitypackage ao Unity usando a opção de menu Pacote personalizado do pacote> de importação de ativos.>
Na caixa Importar pacote Unity que aparece, verifique se tudo em (e incluindo) Plug-ins está selecionado.
Clique no botão Importar para adicionar os itens ao seu projeto.
Vá para a pasta Newtonsoft em Plugins na visualização do projeto e selecione o plug-in Newtonsoft.
Com o plug-in Newtonsoft selecionado, verifique se Any Platform está desmarcado, certifique-se de que o WSAPlayer também esteja desmarcado e clique em Aplicar. Isso é apenas para confirmar que os arquivos estão configurados corretamente.
Nota
Marcar esses plug-ins os configura para serem usados apenas no Editor Unity. Há um conjunto diferente deles na pasta WSA que será usado depois que o projeto for exportado do Unity.
Em seguida, você precisa abrir a pasta WSA , dentro da pasta Newtonsoft . Você verá uma cópia do mesmo arquivo que acabou de configurar. Selecione o arquivo e, em seguida, no inspetor, verifique se
- Qualquer plataforma está desmarcada
- apenas o WSAPlayer está verificado
- O processo não é verificado
Capítulo 7 – Criar o BotTag
Crie um novo objeto Tag chamado BotTag. Selecione a câmera principal na cena. Clique no menu suspenso Tag no painel Inspetor. Clique em Adicionar tag.
Clique no + símbolo. Nomeie a nova tag como BotTag, Save.
Aviso
Não aplique o BotTag na câmera principal. Se você tiver feito isso acidentalmente, certifique-se de alterar a tag da câmera principal de volta para MainCamera.
Capítulo 8 – Criar a classe BotObjects
O primeiro script que você precisa criar é a classe BotObjects , que é uma classe vazia criada para que uma série de outros objetos de classe possam ser armazenados dentro do mesmo script e acessados por outros scripts na cena.
A criação desta classe é puramente uma escolha arquitetônica, esses objetos podem ser hospedados no script Bot que você criará mais tarde neste curso.
Para criar esta classe:
Clique com o botão direito do mouse no painel Projeto e, em seguida , em Criar > pasta. Nomeie a pasta Scripts.
Clique duas vezes na pasta Scripts para abri-la. Em seguida, dentro dessa pasta, clique com o botão direito do mouse e selecione Criar > script C#. Nomeie o script BotObjects.
Clique duas vezes no novo script BotObjects para abri-lo com o Visual Studio.
Exclua o conteúdo do script e substitua-o pelo seguinte código:
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; } }
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 9 – Criar a classe GazeInput
A próxima classe que você vai criar é a classe GazeInput . Esta classe é responsável por:
- Criação de um cursor que representará o olhar do jogador.
- Detetando objetos atingidos pelo olhar do jogador, e segurando uma referência aos objetos detetados.
Para criar esta classe:
Vá para a pasta Scripts que você criou anteriormente.
Clique com o botão direito do mouse dentro da pasta, Create > C# Script. Chame o script de GazeInput.
Clique duas vezes no novo script GazeInput para abri-lo com o Visual Studio.
Insira a seguinte linha na parte superior do nome da classe:
/// <summary> /// Class responsible for the User's gaze interactions /// </summary> [System.Serializable] public class GazeInput : MonoBehaviour
Em seguida, adicione as seguintes variáveis dentro da classe GazeInput, acima do método Start():
[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;
Código para o método Start() deve ser adicionado. Isso será chamado quando a classe inicializar:
/// <summary> /// Start method used upon initialization. /// </summary> internal virtual void Start() { FocusedObject = null; Cursor = CreateCursor(); }
Implemente um método que instancie e configure o cursor de olhar:
/// <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; }
Implemente os métodos que configurarão o Raycast a partir da câmera principal e manterão o controle do objeto focado atual.
/// <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); } } } }
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 10 – Criar a classe Bot
O script que você vai criar agora se chama Bot. Esta é a classe principal do seu aplicativo, ele armazena:
- Suas credenciais de Bot de Aplicativo Web
- O método que coleta os comandos de voz do usuário
- O método necessário para iniciar conversas com o Bot do Aplicativo Web
- O método necessário para enviar mensagens para o Bot do aplicativo Web
Para enviar mensagens para o Serviço de Bot, a co-rotina SendMessageToBot() criará uma atividade, que é um objeto reconhecido pelo Bot Framework como dados enviados pelo usuário.
Para criar esta classe:
Clique duas vezes na pasta Scripts para abri-la.
Clique com o botão direito do mouse dentro da pasta Scripts , clique em Criar > script C#. Nomeie o script Bot.
Clique duas vezes no novo script para abri-lo com o Visual Studio.
Atualize os namespaces para serem iguais aos seguintes, na parte superior da classe Bot :
using Newtonsoft.Json; using System.Collections; using System.Text; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Windows.Speech;
Dentro da classe Bot adicione as seguintes variáveis:
/// <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;
Nota
Certifique-se de inserir sua chave secreta do bot na variável botSecret. Você terá anotado sua Chave Secreta do Bot no início deste curso, no Capítulo 2, etapa 10.
Código para Awake() e Start() agora precisa ser adicionado.
/// <summary> /// Called on Initialization /// </summary> void Awake() { Instance = this; } /// <summary> /// Called immediately after Awake method /// </summary> void Start() { botState = BotState.ReadyToListen; }
Adicione os dois manipuladores que são chamados pelas bibliotecas de fala quando a captura de voz começa e termina. O DictationRecognizer irá parar automaticamente de capturar a voz do usuário quando o usuário parar de falar.
/// <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(); }
O manipulador a seguir coleta o resultado da entrada de voz do usuário e chama a co-rotina responsável por enviar a mensagem para o Serviço de Bot do Aplicativo Web.
/// <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(); }
A seguinte co-rotina é chamada para iniciar uma conversa com o Bot. Você notará que, assim que a chamada de conversa for concluída, ela chamará o SendMessageToCoroutine() passando uma série de parâmetros que definirão a atividade a ser enviada ao Serviço de Bot como uma mensagem vazia. Isso é feito para solicitar que o Serviço de Bot inicie o diálogo.
/// <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")); }
A seguinte co-rotina é chamada para construir a atividade a ser enviada para o Serviço de Bot.
/// <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)); } }
A seguinte co-rotina é chamada para solicitar uma resposta depois de enviar uma atividade para o Serviço de Bot.
/// <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; } }
O último método a ser adicionado a esta classe, é necessário para exibir a mensagem na cena:
/// <summary> /// Set the UI Response Text of the bot /// </summary> internal void SetBotResponseText(string responseString) { SceneOrganiser.Instance.botResponseText.text = responseString; }
Nota
Você pode ver um erro no Unity Editor Console, sobre a falta da classe SceneOrganiser . Desconsidere essa mensagem, pois você criará essa classe mais adiante no tutorial.
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 11 – Criar a classe Interactions
A classe que você vai criar agora se chama Interações. Esta classe é usada para detetar a entrada de toque HoloLens do usuário.
Se o usuário tocar enquanto olha para o objeto Bot na cena e o Bot estiver pronto para ouvir entradas de voz, o objeto Bot mudará de cor para vermelho e começará a ouvir entradas de voz.
Essa classe herda da classe GazeInput e, portanto, é capaz de fazer referência ao método Start() e às variáveis dessa classe, denotadas pelo uso de base.
Para criar esta classe:
Clique duas vezes na pasta Scripts para abri-la.
Clique com o botão direito do mouse dentro da pasta Scripts , clique em Criar > script C#. Nomeie o script Interactions.
Clique duas vezes no novo script para abri-lo com o Visual Studio.
Atualize os namespaces e a herança de classe para que sejam iguais aos seguintes, na parte superior da classe Interações :
using UnityEngine.XR.WSA.Input; public class Interactions : GazeInput {
Dentro da classe Interactions, adicione a seguinte variável:
/// <summary> /// Allows input recognition with the HoloLens /// </summary> private GestureRecognizer _gestureRecognizer;
Em seguida, adicione o método 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(); }
Adicione o manipulador que será acionado quando o usuário executar o gesto de toque na frente da câmera 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()); } } } }
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
Capítulo 12 – Criar a classe SceneOrganiser
A última classe exigida neste laboratório chama-se SceneOrganiser. Essa classe configurará a cena programaticamente, adicionando componentes e scripts à câmera principal e criando os objetos apropriados na cena.
Para criar esta classe:
Clique duas vezes na pasta Scripts para abri-la.
Clique com o botão direito do mouse dentro da pasta Scripts , clique em Criar > script C#. Nomeie o script como SceneOrganiser.
Clique duas vezes no novo script para abri-lo com o Visual Studio.
Dentro da classe SceneOrganiser adicione as seguintes variáveis:
/// <summary> /// Static instance of this class /// </summary> public static SceneOrganiser Instance; /// <summary> /// The 3D text representing the Bot response /// </summary> internal TextMesh botResponseText;
Em seguida, adicione os métodos Awake() e 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(); }
Adicione o seguinte método, responsável por criar o objeto Bot na cena e configurar os parâmetros e componentes:
/// <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"; }
Adicione o seguinte método, responsável por criar o objeto UI na cena, representando as respostas do Bot:
/// <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; }
Certifique-se de salvar suas alterações no Visual Studio antes de retornar ao Unity.
No Editor Unity, arraste o script SceneOrganiser da pasta Scripts para a câmera principal. O componente Organizador de cena agora deve aparecer no objeto da câmera principal, como mostrado na imagem abaixo.
Capítulo 13 – Antes de construir
Para realizar um teste completo do seu aplicativo, você precisará fazer o sideload dele em seu HoloLens. Antes de o fazer, certifique-se de que:
- Todas as configurações mencionadas no Capítulo 4 estão definidas corretamente.
- O script SceneOrganiser é anexado ao objeto Main Camera .
- Na classe Bot, certifique-se de ter inserido sua Chave Secreta do Bot na variável botSecret.
Capítulo 14 – Construção e sideload para o HoloLens
Tudo o que é necessário para a seção Unity deste projeto já foi concluído, então é hora de construí-lo a partir de Unity.
Navegue até Configurações de compilação, Configurações de compilação de arquivo>....
Na janela Configurações de compilação, clique em Compilar.
Se ainda não tiver, marque Unity C# Projects.
Clique em Compilar. Unity irá iniciar uma janela do Explorador de Arquivos, onde você precisa criar e, em seguida, selecionar uma pasta para construir o aplicativo. Crie essa pasta agora e nomeie-a como App. Em seguida, com a pasta App selecionada, clique em Selecionar pasta.
Unity começará a construir seu projeto para a pasta App .
Assim que o Unity terminar de construir (pode levar algum tempo), ele abrirá uma janela do Explorador de Arquivos no local da sua compilação (verifique sua barra de tarefas, pois nem sempre ela aparecerá acima de suas janelas, mas notificará você sobre a adição de uma nova janela).
Capítulo 15 – Implantar no HoloLens
Para implantar no HoloLens:
Você precisará do endereço IP do seu HoloLens (para implantação remota) e para garantir que seu HoloLens esteja no modo de desenvolvedor. Para tal:
- Enquanto estiver a usar o HoloLens, abra as Definições.
- Ir para Rede & Opções Avançadas de Wi-Fi > da Internet >
- Observe o endereço IPv4 .
- Em seguida, navegue de volta para Configurações e, em seguida, para Atualizar & Segurança > para desenvolvedores
- Defina o modo de desenvolvedor ativado.
Navegue até sua nova compilação Unity (a pasta App) e abra o arquivo de solução com o Visual Studio.
Na Configuração da Solução, selecione Depurar.
Na Plataforma de Solução, selecione x86, Máquina Remota.
Vá para o menu Build e clique em Deploy Solution, para fazer sideload do aplicativo para o seu HoloLens.
Seu aplicativo agora deve aparecer na lista de aplicativos instalados no seu HoloLens, pronto para ser iniciado!
Nota
Para implantar no fone de ouvido imersivo, defina a Plataforma de Solução como Máquina Local e defina a Configuração como Depurar, com x86 como Plataforma. Em seguida, implante na máquina local, usando o menu Compilar, selecionando Implantar solução.
Capítulo 16 – Usando o aplicativo no HoloLens
Depois de iniciar o aplicativo, você verá o Bot como uma esfera azul na sua frente.
Use o gesto de toque enquanto olha para a esfera para iniciar uma conversa.
Aguarde até que a conversa comece (A interface do usuário exibirá uma mensagem quando isso acontecer). Depois de receber a mensagem introdutória do Bot, toque novamente no Bot para que ele fique vermelho e comece a ouvir sua voz.
Assim que você parar de falar, seu aplicativo enviará sua mensagem para o Bot e você receberá prontamente uma resposta que será exibida na interface do usuário.
Repita o processo para enviar mais mensagens para o seu Bot (você tem que tocar cada vez que quiser enviar uma mensagem).
Esta conversa demonstra como o Bot pode reter informações (seu nome), ao mesmo tempo em que fornece informações conhecidas (como os itens que estão armazenados).
Algumas perguntas a fazer ao Bot:
what do you sell?
how much are umbrellas?
how much are raincoats?
Seu aplicativo Web App Bot (v4) concluído
Parabéns, você criou um aplicativo de realidade mista que aproveita o Bot do Aplicativo Web do Azure, o Microsoft Bot Framework v4.
Exercícios de bónus
Exercício 1
A estrutura de conversação neste Laboratório é muito básica. Use o Microsoft LUIS para dar ao seu bot recursos de compreensão de linguagem natural.
Exercício 2
Este exemplo não inclui encerrar uma conversa e reiniciar uma nova. Para completar o recurso Bot, tente implementar o fechamento da conversa.