Treinamento
Módulo
Este curso fornece ao usuário uma compreensão básica de todos os elementos fundamentais do MRTK.
Não há mais suporte para esse navegador.
Atualize o Microsoft Edge para aproveitar os recursos, o suporte técnico e as atualizações de segurança mais recentes.
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.
![]() |
![]() |
![]() |
---|---|---|
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. |
Curso | HoloLens | Headsets imersivos |
---|---|---|
Entrada do MR 213: controladores de movimentos | ✔️ |
Confira a lista de verificação de instalação para headsets imersivos nesta página.
Observação
Se você quiser examinar o código-fonte antes de baixar, ele estará disponível no GitHub.
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
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 .
Este tutorial usa Realidade Misturada Toolkit – Unity. Você pode encontrar as versões nesta página.
Cenas concluídas para sua referência
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 :
Há dois pré-fabricados de câmera no kit de ferramentas Realidade Misturada:
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
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:
Neste capítulo, aprenderemos sobre os exemplos dessas personalizações de controlador.
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
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.
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.
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.
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.
Instruções
Script AttachToController
O script AttachToController fornece uma maneira simples de anexar objetos a um elemento e uma entrega do controlador especificados.
Em AttachElementToController(),
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
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;
}
...
}
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
}
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
Script ColorPickerWheel
ColorPickerWheel assina o evento InteractionSourceUpdated do Unity para escutar eventos do touchpad.
Em InteractionSourceUpdated(), o script primeiro verifica para garantir que:
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);
}
O pré-fabricado BrushController não precisa ser incluído no painel Hierarquia . No entanto, para marcar seus componentes filho:
Você encontrará o componente Dica no BrushController. Usaremos sua transformação para iniciar/parar linhas de desenho.
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;
...
}
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.
Você poderá alterar os objetos com o botão Agarrar e gerar objetos com o botão Selecionar.
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.
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 .
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
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:
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.
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.
No painel Hierarquia , exclua MixedRealityCamera, Environment e MotionControllers
No painel Projeto, pesquise e arraste os seguintes pré-fabricados para o painel Hierarquia :
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.
E esse é o fim deste tutorial! Você aprendeu a:
Agora você está pronto para começar a criar sua própria experiência imersiva com controladores de movimento!
Treinamento
Módulo
Este curso fornece ao usuário uma compreensão básica de todos os elementos fundamentais do MRTK.