Entrada do MR 213: controladores de movimentos
Observação
Os tutoriais do Mixed Reality Academy foram projetados com o HoloLens (1ª geração) e os headsets imersivos de realidade misturada em mente. Dessa forma, achamos que é importante continuar disponibilizando esses tutoriais para os desenvolvedores que ainda buscam obter diretrizes para o desenvolvimento visando esses dispositivos. Esses tutoriais não serão atualizados com os conjuntos de ferramentas mais recentes nem com as interações usadas para o HoloLens 2. Eles serão mantidos para continuar funcionando nos dispositivos compatíveis. Uma nova série de tutoriais foi postada para o HoloLens 2.
Os controladores de movimento no mundo da realidade misturada adicionam outro nível de interatividade. Com os controladores de movimento, podemos interagir diretamente com objetos de maneira mais natural, semelhante às nossas interações físicas na vida real, aumentando a imersão e o prazer em sua experiência de aplicativo.
No MR Input 213, exploraremos os eventos de entrada do controlador de movimento criando uma experiência de pintura espacial simples. Com esse aplicativo, os usuários podem pintar em espaço tridimensional com vários tipos de pincéis e cores.
Tópicos abordados neste tutorial
Visualização do controlador | Eventos de entrada do controlador | Controlador personalizado e interface do usuário |
Saiba como renderizar modelos de controlador de movimento no modo de jogo e no runtime do Unity. | Entenda diferentes tipos de eventos de botão e seus aplicativos. | Saiba como sobrepor elementos da interface do usuário sobre o controlador ou personalizá-lo totalmente. |
Suporte a dispositivos
Curso | HoloLens | Headsets imersivos |
---|---|---|
Entrada do MR 213: controladores de movimentos | ✔️ |
Antes de começar
Pré-requisitos
Confira a lista de verificação de instalação para headsets imersivos nesta página.
- Este tutorial requer o Unity 2017.2.1p2
Arquivos de projeto
- Baixe os arquivos exigidos pelo projeto e extraia os arquivos para a Área de Trabalho.
Observação
Se você quiser examinar o código-fonte antes de baixar, ele estará disponível no GitHub.
Configuração do Unity
Objetivos
- Otimizar o Unity para desenvolvimento de Windows Mixed Reality
- Configurar a câmera Realidade Misturada
- Ambiente de configuração
Instruções
Inicie o Unity.
Selecione Abrir.
Navegue até a Área de Trabalho e localize a pasta master MixedReality213 que você desarquivou anteriormente.
Clique em Selecionar Pasta.
Depois que o Unity terminar de carregar arquivos de projeto, você poderá ver o editor do Unity.
No Unity, selecione Configurações de Build de Arquivo>.
Selecione Plataforma Universal do Windows na lista Plataforma e clique no botão Alternar Plataforma.
Definir o dispositivo de destino como qualquer dispositivo
Defina o Tipo de Build como D3D
Definir o SDK como o mais recente instalado
Verificar projetos C# do Unity
- Isso permite modificar arquivos de script no projeto do Visual Studio sem recompilar o projeto do Unity.
Clique em Configurações do Player.
No painel Inspetor , role para baixo até a parte inferior
Em Configurações de XR, marcar realidade virtual com suporte
Em SDKs de Realidade Virtual, selecione Windows Mixed Reality
Feche a janela Configurações de Build .
Estrutura do projeto
Este tutorial usa Realidade Misturada Toolkit – Unity. Você pode encontrar as versões nesta página.
Cenas concluídas para sua referência
- Você encontrará duas cenas concluídas do Unity na pasta Cenas .
- MixedReality213: Cena concluída com pincel único
- MixedReality213Avançado: cena concluída para design avançado com vários pincéis
Configuração de nova cena para o tutorial
No Unity, clique em Arquivo > Nova Cena
Excluir Câmera Principal e Luz Direcional
No painel Projeto, pesquise e arraste os seguintes pré-fabricados para o painel Hierarquia :
- Assets/HoloToolkit/Input/Prefabs/MixedRealityCamera
- Ativos/AppPrefabs/Ambiente
Há dois pré-fabricados de câmera no kit de ferramentas Realidade Misturada:
- MixedRealityCamera.prefab: somente câmera
- MixedRealityCameraParent.prefab: Camera + Teleportation + Boundary
- Neste tutorial, usaremos MixedRealityCamera sem recurso de teletransporte. Por isso, adicionamos um pré-fabricado ambiente simples que contém um piso básico para fazer com que o usuário se sinta aterrado.
- Para saber mais sobre o teletransporte com MixedRealityCameraParent, confira Design avançado – Teletransporte e locomoção
Configuração do Skybox
Clique em Configurações de Iluminação da > Janela >
Clique no círculo no lado direito do campo Material do Skybox
Digite 'cinza' e selecione SkyboxGray (Assets/AppPrefabs/Support/Materials/SkyboxGray.mat)
Marque a opção Skybox para poder ver o skybox de gradiente cinza atribuído
A cena com MixedRealityCamera, Environment e skybox cinza terá esta aparência.
Clique em Arquivo > Salvar Cena como
Salve sua cena na pasta Cenas com qualquer nome
Capítulo 1 – Visualização do controlador
Objetivos
- Saiba como renderizar modelos de controlador de movimento no modo de jogo do Unity e em runtime.
Windows Mixed Reality fornece um modelo de controlador animado para visualização do controlador. Há várias abordagens que você pode adotar para visualização do controlador em seu aplicativo:
- Padrão – Usando o controlador padrão sem modificação
- Híbrido – usando o controlador padrão, mas personalizando alguns de seus elementos ou sobrepondo componentes da interface do usuário
- Substituição – Usando seu próprio modelo 3D personalizado para o controlador
Neste capítulo, aprenderemos sobre os exemplos dessas personalizações de controlador.
Instruções
- No painel Projeto , digite MotionControllers na caixa de pesquisa . Você também pode encontrá-lo em Ativos/HoloToolkit/Input/Prefabs/.
- Arraste a pré-fabricado MotionControllers para o painel Hierarquia .
- Clique na pré-fabricado MotionControllers no painel Hierarquia .
Pré-fabricado MotionControllers
O prefab MotionControllers tem um script MotionControllerVisualizer que fornece os slots para modelos de controlador alternativos. Se você atribuir seus próprios modelos 3D personalizados, como uma mão ou uma espada e marcar "Sempre usar modelo alternativo à esquerda/direita", você os verá em vez do modelo padrão. Usaremos esse slot no Capítulo 4 para substituir o modelo do controlador por um pincel.
Instruções
- No painel Inspetor , clique duas vezes em MotionControllerVisualizer script para ver o código no Visual Studio
Script MotionControllerVisualizer
As classes MotionControllerVisualizer e MotionControllerInfo fornecem os meios para acessar & modificar os modelos de controlador padrão. MotionControllerVisualizer assina o evento InteractionSourceDetected do Unity e cria uma instância automática dos modelos de controlador quando eles são encontrados.
protected override void Awake()
{
...
InteractionManager.InteractionSourceDetected += InteractionManager_InteractionSourceDetected;
InteractionManager.InteractionSourceLost += InteractionManager_InteractionSourceLost;
...
}
Os modelos de controlador são entregues de acordo com a especificação glTF. Esse formato foi criado para fornecer um formato comum, ao mesmo tempo em que aprimora o processo por trás da transmissão e descompactação de ativos 3D. Nesse caso, precisamos recuperar e carregar os modelos de controlador em runtime, pois queremos tornar a experiência do usuário o mais perfeita possível e não é garantido qual versão dos controladores de movimento o usuário pode estar usando. Este curso, por meio do kit de ferramentas Realidade Misturada, usa uma versão do projeto UnityGLTF do Grupo Khronos.
Depois que o controlador for entregue, os scripts poderão usar MotionControllerInfo para encontrar as transformações de elementos de controlador específicos para que possam se posicionar corretamente.
Em um capítulo posterior, aprenderemos a usar esses scripts para anexar elementos de interface do usuário aos controladores.
Em alguns scripts, você encontrará blocos de código com #if ! UNITY_EDITOR ou UNITY_WSA. Esses blocos de código são executados somente no runtime da UWP quando você implanta no Windows. Isso ocorre porque o conjunto de APIs usado pelo editor do Unity e o runtime do aplicativo UWP são diferentes.
- Salve a cena e clique no botão Reproduzir .
Você poderá ver a cena com controladores de movimento em seu fone de ouvido. Você pode ver animações detalhadas para cliques de botão, movimento de polegar e realce de toque do touchpad.
Capítulo 2 – Anexando elementos de interface do usuário ao controlador
Objetivos
- Saiba mais sobre os elementos dos controladores de movimento
- Saiba como anexar objetos a partes específicas dos controladores
Neste capítulo, você aprenderá a adicionar elementos de interface do usuário ao controlador que o usuário pode acessar e manipular facilmente a qualquer momento. Você também aprenderá a adicionar uma interface do usuário do seletor de cores simples usando a entrada touchpad.
Instruções
- No painel Projeto , pesquise MotionControllerInfo script.
- No resultado da pesquisa, clique duas vezes em MotionControllerInfo script para ver o código no Visual Studio.
Script MotionControllerInfo
A primeira etapa é escolher a qual elemento do controlador você deseja que a interface do usuário anexe. Esses elementos são definidos em ControllerElementEnum em MotionControllerInfo.cs.
- Início
- Menu
- Agarrar
- Manípulo
- Selecionar
- Touchpad
- Pose apontando – esse elemento representa a ponta do controlador apontando para a direção para a frente.
Instruções
- No painel Projeto , pesquise o script AttachToController .
- No resultado da pesquisa, clique duas vezes em AnexarToController script para ver o código no Visual Studio.
Script AttachToController
O script AttachToController fornece uma maneira simples de anexar objetos a um elemento e uma entrega do controlador especificados.
Em AttachElementToController(),
- Verificar a entrega usando MotionControllerInfo.Handedness
- Obter elemento específico do controlador usando MotionControllerInfo.TryGetElement()
- Depois de recuperar a transformação do elemento do modelo de controlador, pai do objeto sob ele e definir a posição local do objeto & rotação como zero.
public MotionControllerInfo.ControllerElementEnum Element { get { return element; } }
private void AttachElementToController(MotionControllerInfo newController)
{
if (!IsAttached && newController.Handedness == handedness)
{
if (!newController.TryGetElement(element, out elementTransform))
{
Debug.LogError("Unable to find element of type " + element + " under controller " + newController.ControllerParent.name + "; not attaching.");
return;
}
controller = newController;
SetChildrenActive(true);
// Parent ourselves under the element and set our offsets
transform.parent = elementTransform;
transform.localPosition = positionOffset;
transform.localEulerAngles = rotationOffset;
if (setScaleOnAttach)
{
transform.localScale = scale;
}
// Announce that we're attached
OnAttachToController();
IsAttached = true;
}
}
A maneira mais simples de usar o script AttachToController é herdar dele, como fizemos no caso de ColorPickerWheel. Basta substituir as funções OnAttachToController e OnDetachFromController para executar sua instalação/detalhamento quando o controlador for detectado/desconectado.
Instruções
- No painel Projeto , digite na caixa de pesquisa ColorPickerWheel. Você também pode encontrá-lo em Ativos/AppPrefabs/.
- Arraste a pré-fabricado ColorPickerWheel para o painel Hierarquia .
- Clique na pré-fabricado ColorPickerWheel no painel Hierarquia .
- No painel Inspetor , clique duas vezes em ColorPickerWheel Script para ver o código no Visual Studio.
Script ColorPickerWheel
Como ColorPickerWheel herda AttachToController, ele mostra Handedness e Element no painel Inspetor . Anexaremos a interface do usuário ao elemento Touchpad no controlador esquerdo.
ColorPickerWheel substitui OnAttachToController e OnDetachFromController para assinar o evento de entrada que será usado no próximo capítulo para seleção de cores com entrada touchpad.
public class ColorPickerWheel : AttachToController, IPointerTarget
{
protected override void OnAttachToController()
{
// Subscribe to input now that we're parented under the controller
InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
}
protected override void OnDetachFromController()
{
Visible = false;
// Unsubscribe from input now that we've detached from the controller
InteractionManager.InteractionSourceUpdated -= InteractionSourceUpdated;
}
...
}
- Salve a cena e clique no botão Reproduzir .
Método alternativo para anexar objetos aos controladores
Recomendamos que seus scripts herdem de AttachToController e substituam OnAttachToController. No entanto, isso pode nem sempre ser possível. Uma alternativa é usá-la como um componente autônomo. Isso pode ser útil quando você deseja anexar um pré-fabricado existente a um controlador sem refatorar seus scripts. Basta fazer com que sua classe aguarde que IsAttached seja definido como true antes de executar qualquer configuração. A maneira mais simples de fazer isso é usando uma corrotina para "Iniciar".
private IEnumerator Start() {
AttachToController attach = gameObject.GetComponent<AttachToController>();
while (!attach.IsAttached) {
yield return null;
}
// Perform setup here
}
Capítulo 3 – Trabalhando com entrada touchpad
Objetivos
- Saiba como obter eventos de dados de entrada do touchpad
- Saiba como usar informações de posição do eixo touchpad para sua experiência de aplicativo
Instruções
- No painel Hierarquia , clique em ColorPickerWheel
- No painel Inspetor , em Animador, clique duas vezes em ColorPickerWheelController
- Você poderá ver a guia Animador aberta
Mostrando/ocultando a interface do usuário com o controlador de animação do Unity
Para mostrar e ocultar a interface do usuário ColorPickerWheel com animação, estamos usando o sistema de animação do Unity. Definindo a propriedade Visible do ColorPickerWheel como gatilhos verdadeiros ou falsos Mostrar e Ocultar gatilhos de animação. Os parâmetros Mostrar e Ocultar são definidos no controlador de animação ColorPickerWheelController .
Instruções
- No painel Hierarquia , selecione ColorPickerWheel pré-fabricado
- No painel Inspetor , clique duas vezes em ColorPickerWheel script para ver o código no Visual Studio
Script ColorPickerWheel
ColorPickerWheel assina o evento InteractionSourceUpdated do Unity para escutar eventos do touchpad.
Em InteractionSourceUpdated(), o script primeiro verifica para garantir que:
- é, na verdade, um evento touchpad (obj.state).touchpadTouched)
- origina-se do controlador esquerdo (obj.state.source.handedness)
Se ambos forem verdadeiros, a posição do touchpad (obj.state.touchpadPosition) é atribuído a selectorPosition.
private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
if (obj.state.source.handedness == handedness && obj.state.touchpadTouched)
{
Visible = true;
selectorPosition = obj.state.touchpadPosition;
}
}
Em Update(), com base na propriedade visível , ela dispara mostrar e ocultar gatilhos de animação no componente do animador do seletor de cores
if (visible != visibleLastFrame)
{
if (visible)
{
animator.SetTrigger("Show");
}
else
{
animator.SetTrigger("Hide");
}
}
Em Update(), selectorPosition é usado para converter um raio no colisor de malha da roda colorida, que retorna uma posição UV. Essa posição pode ser usada para localizar a coordenada de pixel e o valor de cor da textura da roda de cor. Esse valor é acessível a outros scripts por meio da propriedade SelectedColor .
...
// Clamp selector position to a radius of 1
Vector3 localPosition = new Vector3(selectorPosition.x * inputScale, 0.15f, selectorPosition.y * inputScale);
if (localPosition.magnitude > 1)
{
localPosition = localPosition.normalized;
}
selectorTransform.localPosition = localPosition;
// Raycast the wheel mesh and get its UV coordinates
Vector3 raycastStart = selectorTransform.position + selectorTransform.up * 0.15f;
RaycastHit hit;
Debug.DrawLine(raycastStart, raycastStart - (selectorTransform.up * 0.25f));
if (Physics.Raycast(raycastStart, -selectorTransform.up, out hit, 0.25f, 1 << colorWheelObject.layer, QueryTriggerInteraction.Ignore))
{
// Get pixel from the color wheel texture using UV coordinates
Vector2 uv = hit.textureCoord;
int pixelX = Mathf.FloorToInt(colorWheelTexture.width * uv.x);
int pixelY = Mathf.FloorToInt(colorWheelTexture.height * uv.y);
selectedColor = colorWheelTexture.GetPixel(pixelX, pixelY);
selectedColor.a = 1f;
}
// Set the selector's color and blend it with white to make it visible on top of the wheel
selectorRenderer.material.color = Color.Lerp (selectedColor, Color.white, 0.5f);
}
Capítulo 4 – Substituindo o modelo de controlador
Objetivos
- Saiba como substituir o modelo do controlador por um modelo 3D personalizado.
Instruções
- Clique em MotionControllers no painel Hierarquia .
- Clique no círculo no lado direito do campo Controlador Direito Alternativo .
- Digite 'BrushController' e selecione o pré-fabricado no resultado. Você pode encontrá-lo em Ativos/AppPrefabs/BrushController.
- Verificar Sempre Usar Modelo Alternativo à Direita
O pré-fabricado BrushController não precisa ser incluído no painel Hierarquia . No entanto, para marcar seus componentes filho:
- No painel Projeto , digite BrushController e arraste o pré-fabricado BrushController para o painel Hierarquia .
Você encontrará o componente Dica no BrushController. Usaremos sua transformação para iniciar/parar linhas de desenho.
- Exclua o BrushController do painel Hierarquia .
- Salve a cena e clique no botão Reproduzir . Você poderá ver que o modelo de pincel substituiu o controlador de movimento à direita.
Capítulo 5 – Pintando com a entrada Selecionar
Objetivos
- Saiba como usar o evento do botão Selecionar para iniciar e parar um desenho de linha
Instruções
- Pesquise o pré-fabricado BrushController no painel Projeto .
- No painel Inspetor , clique duas vezes em Script BrushController para ver o código no Visual Studio
Script BrushController
BrushController assina os eventos InteractionSourcePressed e InteractionSourceReleased do InteractionManager. Quando o evento InteractionSourcePressed é disparado, a propriedade Draw do pincel é definida como true; quando o evento InteractionSourceReleased é disparado, a propriedade Draw do pincel é definida como false.
private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
{
Draw = true;
}
}
private void InteractionSourceReleased(InteractionSourceReleasedEventArgs obj)
{
if (obj.state.source.handedness == InteractionSourceHandedness.Right && obj.pressType == InteractionSourcePressType.Select)
{
Draw = false;
}
}
Embora Draw seja definido como true, o pincel gerará pontos em um LineRenderer do Unity instanciado. Uma referência a esse pré-fabricado é mantida no campo Prefab stroke do pincel.
private IEnumerator DrawOverTime()
{
// Get the position of the tip
Vector3 lastPointPosition = tip.position;
...
// Create a new brush stroke
GameObject newStroke = Instantiate(strokePrefab);
LineRenderer line = newStroke.GetComponent<LineRenderer>();
newStroke.transform.position = startPosition;
line.SetPosition(0, tip.position);
float initialWidth = line.widthMultiplier;
// Generate points in an instantiated Unity LineRenderer
while (draw)
{
// Move the last point to the draw point position
line.SetPosition(line.positionCount - 1, tip.position);
line.material.color = colorPicker.SelectedColor;
brushRenderer.material.color = colorPicker.SelectedColor;
lastPointAddedTime = Time.unscaledTime;
// Adjust the width between 1x and 2x width based on strength of trigger pull
line.widthMultiplier = Mathf.Lerp(initialWidth, initialWidth * 2, width);
if (Vector3.Distance(lastPointPosition, tip.position) > minPositionDelta || Time.unscaledTime > lastPointAddedTime + maxTimeDelta)
{
// Spawn a new point
lastPointAddedTime = Time.unscaledTime;
lastPointPosition = tip.position;
line.positionCount += 1;
line.SetPosition(line.positionCount - 1, lastPointPosition);
}
yield return null;
}
}
Para usar a cor selecionada no momento na interface do usuário do seletor de cores, BrushController precisa ter uma referência ao objeto ColorPickerWheel . Como o pré-fabricado BrushController é instanciado em runtime como um controlador de substituição, todas as referências a objetos na cena precisarão ser definidas em runtime. Nesse caso, usamos GameObject.FindObjectOfType para localizar o ColorPickerWheel:
private void OnEnable()
{
// Locate the ColorPickerWheel
colorPicker = FindObjectOfType<ColorPickerWheel>();
// Assign currently selected color to the brush’s material color
brushRenderer.material.color = colorPicker.SelectedColor;
...
}
- Salve a cena e clique no botão Reproduzir . Você poderá desenhar as linhas e pintar usando o botão selecionar no controlador direito.
Capítulo 6 – Geração de objetos com a opção Selecionar entrada
Objetivos
- Saiba como usar eventos de entrada do botão Selecionar e Entender
- Saiba como instanciar objetos
Instruções
No painel Projeto , digite ObjectSpawner na caixa de pesquisa. Você também pode encontrá-lo em Ativos/AppPrefabs/
Arraste o pré-fabricado ObjectSpawner para o painel Hierarquia .
Clique em ObjetoPawner no painel Hierarquia .
ObjectSpawner tem um campo chamado Fonte de Cores.
No painel Hierarquia , arraste a referência ColorPickerWheel para esse campo.
Clique no pré-fabricado ObjectSpawner no painel Hierarquia .
No painel Inspetor , clique duas vezes em ObjectSpawner Script para ver o código no Visual Studio.
Script ObjectSpawner
O ObjectSpawner cria uma instância de cópias de uma malha primitiva (cubo, esfera, cilindro) no espaço. Quando um InteractionSourcePressed é detectado, ele verifica a entrega e se é um evento InteractionSourcePressType.Grasp ou InteractionSourcePressType.Select .
Para um evento Grasp , ele incrementa o índice do tipo de malha atual (esfera, cubo, cilindro)
private void InteractionSourcePressed(InteractionSourcePressedEventArgs obj)
{
// Check handedness, see if it is left controller
if (obj.state.source.handedness == handedness)
{
switch (obj.pressType)
{
// If it is Select button event, spawn object
case InteractionSourcePressType.Select:
if (state == StateEnum.Idle)
{
// We've pressed the grasp - enter spawning state
state = StateEnum.Spawning;
SpawnObject();
}
break;
// If it is Grasp button event
case InteractionSourcePressType.Grasp:
// Increment the index of current mesh type (sphere, cube, cylinder)
meshIndex++;
if (meshIndex >= NumAvailableMeshes)
{
meshIndex = 0;
}
break;
default:
break;
}
}
}
Para um evento Select , em SpawnObject(), um novo objeto é instanciado, não pai e liberado no mundo.
private void SpawnObject()
{
// Instantiate the spawned object
GameObject newObject = Instantiate(displayObject.gameObject, spawnParent);
// Detach the newly spawned object
newObject.transform.parent = null;
// Reset the scale transform to 1
scaleParent.localScale = Vector3.one;
// Set its material color so its material gets instantiated
newObject.GetComponent<Renderer>().material.color = colorSource.SelectedColor;
}
O ObjectSpawner usa ColorPickerWheel para definir a cor do material do objeto de exibição. Os objetos gerados recebem uma instância desse material para que eles mantenham sua cor.
- Salve a cena e clique no botão Reproduzir .
Você poderá alterar os objetos com o botão Agarrar e gerar objetos com o botão Selecionar.
Criar e implantar o aplicativo no Portal do Realidade Misturada
- No Unity, selecione Configurações de Build de Arquivo>.
- Clique em Adicionar Cenas Abertas para adicionar a cena atual ao Scenes In Build.
- Clique em Compilar.
- Crie uma nova pasta chamada "App".
- Clique com um único clique na pasta Aplicativo .
- Clique em Selecionar Pasta.
- Quando o Unity for concluído, uma janela Explorador de Arquivos será exibida.
- Abra a pasta Aplicativo .
- Clique duas vezes no arquivo Solução YourSceneName.sln do Visual Studio.
- Usando a barra de ferramentas superior no Visual Studio, altere o destino de Depurar para Versão e de ARM para X64.
- Clique na seta suspensa ao lado do botão Dispositivo e selecione Computador Local.
- Clique em Depurar –> Iniciar Sem depuração no menu ou pressione Ctrl + F5.
Agora, o aplicativo foi criado e instalado no Portal do Realidade Misturada. Você pode iniciá-lo novamente por meio do menu Iniciar no Portal Realidade Misturada.
Design avançado – Ferramentas de pincel com layout radial
Neste capítulo, você aprenderá a substituir o modelo de controlador de movimento padrão por uma coleção de ferramentas de pincel personalizada. Para sua referência, você pode encontrar a cena concluída MixedReality213Avançada na pasta Cenas .
Instruções
No painel Projeto , digite BrushSelector na caixa de pesquisa . Você também pode encontrá-lo em Ativos/AppPrefabs/
Arraste o pré-fabricado BrushSelector para o painel Hierarquia .
Para a organização, crie um GameObject vazio chamado Brushes
Arraste os pré-fabricados a seguir do painel Projeto para Pincéis
- Assets/AppPrefabs/BrushFat
- Assets/AppPrefabs/BrushThin
- Assets/AppPrefabs/Eraser
- Assets/AppPrefabs/MarkerFat
- Assets/AppPrefabs/MarkerThin
- Assets/AppPrefabs/Pencil
Clique em MotionControllers pré-fabricado no painel Hierarquia .
No painel Inspetor , desmarque Sempre Usar Modelo Alternativo à Direita no Visualizador do Controlador de Movimento
No painel Hierarquia , clique em BrushSelector
BrushSelector tem um campo chamado ColorPicker
No painel Hierarquia , arraste o ColorPickerWheel para o campo ColorPicker no painel Inspetor .
No painel Hierarquia , em Pré-fabricado BrushSelector , selecione o objeto Menu .
No painel Inspetor , no componente LineObjectCollection , abra a lista suspensa Matriz de objetos . Você verá seis slots vazios.
No painel Hierarquia , arraste cada um dos pré-fabricados com pai sob o GameObject Brushes para esses slots em qualquer ordem. (Verifique se você está arrastando os pré-fabricados da cena, não os pré-fabricados na pasta do projeto.)
Pré-fabricado BrushSelector
Como BrushSelector herda AttachToController, ele mostra as opções Handedness e Element no painel Inspetor . Selecionamos Posição Direita e Apontando para anexar ferramentas de pincel ao controlador de mão direita com direção para a frente.
O BrushSelector usa dois utilitários:
- Elipse: usada para gerar pontos no espaço ao longo de uma forma de elipse.
- LineObjectCollection: distribui objetos usando os pontos gerados por qualquer classe Line (por exemplo, Ellipse). Isso é o que usaremos para colocar nossos pincéis ao longo da forma de Elipse.
Quando combinados, esses utilitários podem ser usados para criar um menu radial.
Script LineObjectCollection
LineObjectCollection tem controles para o tamanho, posição e rotação de objetos distribuídos ao longo de sua linha. Isso é útil para criar menus radiais como o seletor de pincel. Para criar a aparência de pincéis que são escalados do nada à medida que se aproximam da posição selecionada central, a curva ObjectScale atinge o pico no centro e toca para fora nas bordas.
Script BrushSelector
No caso do BrushSelector, escolhemos usar a animação de procedimento. Primeiro, os modelos de pincel são distribuídos em uma elipse pelo script LineObjectCollection . Em seguida, cada pincel é responsável por manter sua posição na mão do usuário com base em seu valor DisplayMode , que é alterado com base na seleção. Escolhemos uma abordagem de procedimento devido à alta probabilidade de transições de posição do pincel serem interrompidas à medida que o usuário seleciona pincéis. Animações mecanim podem lidar com interrupções normalmente, mas tende a ser mais complicada do que uma simples operação lerp.
BrushSelector usa uma combinação de ambos. Quando a entrada do touchpad é detectada, as opções de pincel ficam visíveis e escalam verticalmente ao longo do menu radial. Após um período de tempo limite (que indica que o usuário fez uma seleção), as opções de pincel reduzem verticalmente novamente, deixando apenas o pincel selecionado.
Visualizando a entrada do touchpad
Mesmo nos casos em que o modelo do controlador foi completamente substituído, pode ser útil mostrar a entrada nas entradas do modelo original. Isso ajuda a fundamentar as ações do usuário na realidade. Para o BrushSelector , escolhemos tornar o touchpad brevemente visível quando a entrada é recebida. Isso foi feito recuperando o elemento Touchpad do controlador, substituindo seu material por um material personalizado e aplicando um gradiente à cor desse material com base na última vez em que a entrada do touchpad foi recebida.
protected override void OnAttachToController()
{
// Turn off the default controller's renderers
controller.SetRenderersVisible(false);
// Get the touchpad and assign our custom material to it
Transform touchpad;
if (controller.TryGetElement(MotionControllerInfo.ControllerElementEnum.Touchpad, out touchpad))
{
touchpadRenderer = touchpad.GetComponentInChildren<MeshRenderer>();
originalTouchpadMaterial = touchpadRenderer.material;
touchpadRenderer.material = touchpadMaterial;
touchpadRenderer.enabled = true;
}
// Subscribe to input now that we're parented under the controller
InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
}
private void Update()
{
...
// Update our touchpad material
Color glowColor = touchpadColor.Evaluate((Time.unscaledTime - touchpadTouchTime) / touchpadGlowLossTime);
touchpadMaterial.SetColor("_EmissionColor", glowColor);
touchpadMaterial.SetColor("_Color", glowColor);
...
}
Seleção de ferramenta de pincel com entrada touchpad
Quando o seletor de pincel detecta a entrada pressionada do touchpad, ele verifica a posição da entrada para determinar se ela estava à esquerda ou à direita.
Espessura do traço com selectPressedAmount
Em vez do evento InteractionSourcePressType.Select no InteractionSourcePressed(), você pode obter o valor analógico do valor pressionado por meio de selectPressedAmount. Esse valor pode ser recuperado em InteractionSourceUpdated().
private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs obj)
{
if (obj.state.source.handedness == handedness)
{
if (obj.state.touchpadPressed)
{
// Check which side we clicked
if (obj.state.touchpadPosition.x < 0)
{
currentAction = SwipeEnum.Left;
}
else
{
currentAction = SwipeEnum.Right;
}
// Ping the touchpad material so it gets bright
touchpadTouchTime = Time.unscaledTime;
}
if (activeBrush != null)
{
// If the pressed amount is greater than our threshold, draw
if (obj.state.selectPressedAmount >= selectPressedDrawThreshold)
{
activeBrush.Draw = true;
activeBrush.Width = ProcessSelectPressedAmount(obj.state.selectPressedAmount);
}
else
{
// Otherwise, stop drawing
activeBrush.Draw = false;
selectPressedSmooth = 0f;
}
}
}
}
Script de borracha
Eraser é um tipo especial de pincel que substitui a função DrawOverTime() do Brush base. Embora Draw seja true, a borracha verifica se sua ponta se cruza com quaisquer traços de pincel existentes. Se isso acontecer, eles serão adicionados a uma fila para serem reduzidos e excluídos.
Design avançado – Teletransporte e locomoção
Se você quiser permitir que o usuário se mova pela cena com teletransporte usando o thumbstick, use MixedRealityCameraParent em vez de MixedRealityCamera. Você também precisa adicionar InputManager e DefaultCursor. Como MixedRealityCameraParent já inclui MotionControllers e Boundary como componentes filho, você deve remover motioncontrollers e pré-fabricados de ambiente existentes.
Instruções
No painel Hierarquia , exclua MixedRealityCamera, Environment e MotionControllers
No painel Projeto, pesquise e arraste os seguintes pré-fabricados para o painel Hierarquia :
- Assets/AppPrefabs/Input/Prefabs/MixedRealityCameraParent
- Assets/AppPrefabs/Input/Prefabs/InputManager
- Assets/AppPrefabs/Input/Prefabs/Cursor/DefaultCursor
No painel Hierarquia , clique em Gerenciador de Entradas
No painel Inspetor, role para baixo até a seção Seletor de Ponteiro Único Simples
No painel Hierarquia, arraste DefaultCursor para o campo Cursor
Salve a cena e clique no botão Reproduzir . Você poderá usar o botão para girar para a esquerda/direita ou teletransporte.
Fim
E esse é o fim deste tutorial! Você aprendeu a:
- Como trabalhar com modelos de controlador de movimento no modo de jogo e no runtime do Unity.
- Como usar diferentes tipos de eventos de botão e seus aplicativos.
- Como sobrepor elementos da interface do usuário sobre o controlador ou personalizá-lo totalmente.
Agora você está pronto para começar a criar sua própria experiência imersiva com controladores de movimento!
Cenas concluídas
- No painel Projeto do Unity, clique na pasta Cenas .
- Você encontrará duas cenas do Unity MixedReality213 e MixedReality213Avançadas.
- MixedReality213: Cena concluída com pincel único
- MixedReality213Avançado: cena concluída com vários pincels com o exemplo de quantidade de pressionamento do botão de seleção