Manipulação em tempo real de malhas na unidade

imagem

Uma das vantagens do Unity como plataforma para o desenvolvimento de jogos é seu poderoso mecanismo 3D. Neste tutorial, você aprenderá sobre o mundo dos objetos 3D e manipulação de malhas.

Devido ao crescimento das tecnologias de realidade virtual e aumentada (VR / AR), a maioria dos desenvolvedores enfrenta conceitos complexos de gráficos 3D. Deixe este tutorial ser o ponto de partida para eles. Não se preocupe, não haverá matemática 3D complicada - apenas corações, desenhos, setas e muitas coisas interessantes!

Nota: este tutorial é destinado a usuários familiarizados com o Unity IDE e com alguma experiência em programação em C #. Se você não tiver esse conhecimento, primeiro estude os tutoriais Introdução à interface do usuário do Unity e Introdução ao script do Unity .

Você precisará de uma versão do Unity não inferior a 2017.3.1. A versão mais recente do Unity pode ser baixada aqui . Este tutorial usa um editor personalizado e você pode aprender mais sobre eles no tutorial Estendendo o Unity Editor .

Começando a trabalhar


Para começar, familiarize-se com os termos básicos dos gráficos 3D, que permitirão entender melhor o tutorial.

Termos técnicos básicos dos gráficos 3D:

  • Vértices : Cada vértice é um ponto no espaço 3D.
  • Malha : contém todos os vértices, arestas, triângulos, normais e dados UV do modelo.
  • Filtro de malha : armazena dados de malha de modelo.
  • Renderizador de malha : renderiza os dados de malha na cena.
  • Normais : o vetor de um vértice ou superfície. É direcionado para o exterior, perpendicular à superfície da malha.
  • Linhas / arestas : linhas invisíveis conectando vértices entre si.
  • Triângulos : formados pela conexão de três picos.
  • Mapa UV : anexa material a um objeto, criando uma textura e cor para ele.

A anatomia de um objeto 3D começa com sua malha. A criação dessa malha começa no topo. Linhas invisíveis que conectam esses vértices formam triângulos que definem a forma básica do objeto.


Os dados normais e UV definem o sombreamento, a cor e a textura. Os dados de malha são armazenados em um filtro de malha e o renderizador de malha usa esses dados para desenhar um objeto na cena.

Ou seja, o pseudocódigo para criar um modelo 3D se parece com o seguinte:

  • Crie uma nova malha chamada "myMesh".
  • Adicione dados às propriedades dos vértices e triângulos myMesh.
  • Crie um novo filtro de malha chamado "myMeshFilter".
  • Defina a propriedade de malha myMeshFilter como myMesh.

Depois de dominar o básico, faça o download do projeto , descompacte os arquivos e execute a peça de trabalho do projeto no Unity. Veja a estrutura da pasta na janela Projeto :


Descrição das pastas:

  • Pré - fabricados : contém o pré-fabricado Sphere que será usado para salvar a malha 3D durante a execução do aplicativo.
  • Cenas : contém as três cenas que usamos neste tutorial.
  • Editor : os scripts dentro desta pasta nos fornecem os super recursos do editor que usamos no desenvolvimento.
  • Scripts : aqui estão os scripts de tempo de execução anexados ao GameObject e executados quando você clica em Play .
  • Materiais : esta pasta contém o material para a malha.

Na próxima seção, criaremos um editor personalizado para visualizar a criação de uma malha 3D.

Alterar malhas com o Editor personalizado


Abra a 01 Demonstração do Estudo de Malha, localizada na pasta Cenas . Na janela Cena , você verá um cubo 3D:


Antes de entrarmos na malha, vamos dar uma olhada no script do editor personalizado.

Editando um Script do Editor


Selecione a pasta Editor na janela Projeto . Os scripts nesta pasta adicionam funcionalidade ao editor (Editor) durante o desenvolvimento e não estão disponíveis no modo Compilar.


Abra o MeshInspector.cs e visualize o código fonte. Todos os scripts do Editor devem implementar a classe Editor , seu atributo CustomEditor informa à classe Editor que tipo de objeto ele é. OnSceneGUI() é um método de evento que permite a renderização na janela Scene; OnInspectorGUI() permite adicionar elementos adicionais da GUI ao Inspector.

No MeshInspector.cs, antes de iniciar a classe MeshInspector adicione o seguinte:

 [CustomEditor(typeof(MeshStudy))] 

Explicação do código: O atributo CustomEditor diz ao Unity que tipo de objeto a classe do editor personalizado pode modificar.

Em OnSceneGUI() antes de EditMesh() adicione o seguinte:

 mesh = target as MeshStudy; Debug.Log("Custom editor is running"); 

Explicação do código: A classe Editor possui uma variável de target padrão. Aqui, o target é uma conversão para o MeshStudy . Agora, o editor personalizado desenhará todos os GameObjects na janela Scene e o MeshStudy.cs anexado a eles. Adicionar mensagens de depuração permite verificar no console que o editor personalizado está realmente em execução.

Salve o arquivo e retorne ao Unity. Vá para a pasta Scripts e arraste o MeshStudy.cs para o cubo GameObject na hierarquia para anexá-lo.


Agora a mensagem "O editor personalizado está em execução" deve ser exibida no console, e isso significa que fizemos tudo certo! Você pode excluir a mensagem de depuração para que ela não nos incomode no console.

Clonagem e descarte da malha


Ao trabalhar com uma malha 3D no modo Editar usando o editor personalizado, tome cuidado para não substituir a malha padrão do Unity. Se isso acontecer, você terá que reiniciar o Unity.

Para clonar com segurança a malha sem substituir o formulário original, crie uma cópia da malha na propriedade MeshFilter.sharedmesh e atribua-a ao filtro de malha novamente.

Para fazer isso, clique duas vezes em MeshStudy.cs na pasta Scripts para abrir o arquivo no editor de código. Esse script é MonoBehaviour classe MonoBehaviour e sua função Start() não é executada no modo de edição.

No MeshStudy.cs, antes de iniciar a classe MeshStudy adicione o seguinte:

 [ExecuteInEditMode] 

Explicação do código: após adicionar este atributo, a função Start() será executada no modo Play e no modo Edit. Agora, podemos instanciar o objeto de malha e cloná-lo.

No InitMesh() adicione o seguinte código:

 oMeshFilter = GetComponent<MeshFilter>(); oMesh = oMeshFilter.sharedMesh; //1 cMesh = new Mesh(); //2 cMesh.name = "clone"; cMesh.vertices = oMesh.vertices; cMesh.triangles = oMesh.triangles; cMesh.normals = oMesh.normals; cMesh.uv = oMesh.uv; oMeshFilter.mesh = cMesh; //3 vertices = cMesh.vertices; //4 triangles = cMesh.triangles; isCloned = true; Debug.Log("Init & Cloned"); 

Código Explicação:

  1. Obtém a malha oMesh original do componente MeshFilter .
  2. Copia o cMesh para uma nova cMesh malha.
  3. Atribui o filtro de malha de malha copiado novamente.
  4. Atualiza variáveis ​​locais.

Salve o arquivo e retorne ao Unity. A mensagem "Init & Cloned" deve ser exibida no console de depuração. Selecione o Cube GameObject na hierarquia e verifique suas propriedades no inspetor . O filtro de malha deve exibir um ativo de malha chamado clone . Ótimo! Isso significa que clonamos a malha com sucesso.


Na pasta Editor, navegue para MeshInspector.cs . No OnInspectorGUI() , após a segunda linha de código, adicione o seguinte:

 if (GUILayout.Button("Reset")) //1 { mesh.Reset(); //2 } 

Código Explicação:

  1. Este código chama um botão Redefinir no Inspetor .
  2. Quando pressionado, ele chama a função Reset() no MeshStudy.cs .

Salve o arquivo, abra o MeshStudy.cs e adicione o seguinte código à função Reset() :

 if (cMesh != null && oMesh != null) //1 { cMesh.vertices = oMesh.vertices; //2 cMesh.triangles = oMesh.triangles; cMesh.normals = oMesh.normals; cMesh.uv = oMesh.uv; oMeshFilter.mesh = cMesh; //3 vertices = cMesh.vertices; //4 triangles = cMesh.triangles; } 

Código Explicação:

  1. Verificando a existência da fonte e da malha clonada.
  2. Redefina o cMesh para a malha original.
  3. Atribuição para cMesh oMeshFilter .
  4. Atualizando variáveis ​​locais.

Salve o arquivo e retorne ao Unity. No Inspetor, clique no botão Editar teste para distorcer a malha do cubo. Em seguida, clique no botão Redefinir ; o cubo deve retornar à sua forma original.


Explicação de vértices e triângulos no Unity


Uma malha consiste em vértices conectados por arestas em triângulos. Triângulos definem a forma básica do objeto.

Classe de malha:

  • Os vértices são armazenados como uma matriz de valores Vector3 .
  • Os triângulos são armazenados como uma matriz inteira correspondente aos índices da matriz de vértices.

Ou seja, em uma malha Quad simples, composta por quatro vértices e dois triângulos, os dados da malha terão a seguinte aparência:


Mapeamento de vértices


Aqui queremos exibir os vértices do cubo como pontos azuis.

No MeshInspector.cs , iremos para a função EditMesh() e adicionaremos o seguinte:

 handleTransform = mesh.transform; //1 handleRotation = Tools.pivotRotation == PivotRotation.Local ? handleTransform.rotation : Quaternion.identity; //2 for (int i = 0; i < mesh.vertices.Length; i++) //3 { ShowPoint(i); } 

Código Explicação:

  1. handleTransform obtém valores de transformação da mesh .
  2. handleRotation obtém o modo Rotação da junta atual.
  3. Atravesse os vértices da malha e desenhe os pontos usando ShowPoint() .

Na função ShowPoint() , imediatamente após o //draw dot comentário do //draw dot , adicione o seguinte:

 Vector3 point = handleTransform.TransformPoint(mesh.vertices[index]); 

Código Explicação: Esta linha converte a posição local do vértice em uma coordenada no espaço do mundo.

Na mesma função, no bloco if , imediatamente após a linha de código adicionada, adicione o seguinte:

 Handles.color = Color.blue; point = Handles.FreeMoveHandle(point, handleRotation, mesh.handleSize, Vector3.zero, Handles.DotHandleCap); 

Código Explicação:

  1. Define a cor, tamanho e posição de um ponto usando a classe auxiliar Handles .
  2. Handles.FreeMoveHandle() cria um manipulador de movimento ilimitado que simplifica a operação de arrastar e soltar, o que é útil para nós na próxima seção.

Salve o arquivo e retorne ao Unity. Verifique a propriedade do cubo no Inspetor e verifique se a opção Mover ponto de vértice está ativada. Agora você deve ver que a malha na tela está marcada com vários pontos azuis. Aqui estão eles - os topos da malha do cubo! Tente fazer isso com outros objetos 3D e observe os resultados.


Mover um único vértice


Vamos começar com o passo mais simples de manipular a malha - movendo um único vértice.

Vá para MeshInspector.cs . Dentro da função ShowPoint() , imediatamente após o //drag comentário e imediatamente antes dos colchetes de fechamento do bloco if , adicione o seguinte:

 if (GUI.changed) //1 { mesh.DoAction(index, handleTransform.InverseTransformPoint(point)); //2 } 

Código Explicação:

  1. GUI.changed controla todas as alterações que ocorrem com pontos e funciona bem em conjunto com Handles.FreeMoveHandle() para reconhecer uma operação de arrastar e soltar.
  2. Para o vértice arrastável, a função mesh.DoAction() recebe seu índice e valores de transformação como parâmetros. Como os valores Transform do vértice estão no espaço mundial, os convertemos em espaço local usando InverseTransformPoint() .

Salve o arquivo de script e vá para MeshStudy.cs . Em DoAction() , após os colchetes de abertura, adicione o seguinte:

 PullOneVertex(index, localPos); 

Em seguida, adicione o seguinte à função PullOneVertex() :

 vertices[index] = newPos; //1 cMesh.vertices = vertices; //2 cMesh.RecalculateNormals(); //3 

Código Explicação:

  1. Atualizamos o vértice de destino com o valor newPos .
  2. cMesh.vertices valores atualizados de vértice de volta a cMesh.vertices .
  3. Em RecalculateNormals() recalculamos e redesenhamos a malha para que ela corresponda às alterações.

Salve o arquivo e retorne ao Unity. Tente arrastar pontos no cubo; você viu uma malha quebrada?


Parece que alguns dos vértices têm a mesma posição; portanto, quando arrastamos apenas um, os vértices restantes permanecem atrás dele e a malha quebra. Na próxima seção, corrigiremos esse problema.

Localizando todos os vértices semelhantes


Visualmente, uma malha de cubo consiste em oito vértices, seis lados e 12 triângulos. Vamos verificar se é assim.


Abra o MeshStudy.cs , dê uma olhada na frente da função Start() e encontre a variável de vertices . Veremos o seguinte:

 [HideInInspector] public Vector3[] vertices; 

Explicação do código: [HideInInspector] oculta uma variável compartilhada da janela Inspetor .

Comente este atributo:

 //[HideInInspector] public Vector3[] vertices; 

Nota: ocultar valores de vértices ajuda o [HideInInspector] com malhas 3D mais complexas. Como o tamanho da matriz de vértices pode atingir milhares de elementos, isso pode levar à inibição do Unity ao tentar visualizar o valor da matriz no Inspector.

Salve o arquivo e retorne ao Unity. Vá para o Inspetor . Agora, no componente de script Estudo de malha , a propriedade vértices apareceu. Clique no ícone de seta ao lado; para Vector3 matriz de elementos Vector3 .


Você pode ver que o tamanho da matriz é 24, ou seja, existem vértices com a mesma posição! Antes de continuar, certifique-se de descomentar [HideInInspector] .

Por que existem 24 vértices?
Existem muitas teorias sobre esse assunto. Mas a resposta mais simples é: o cubo tem seis lados e cada lado é composto por quatro vértices que formam um plano.

Portanto, o cálculo é o seguinte: 6 x 4 = 24 vértices.

Você pode procurar outras respostas. Mas, por enquanto, é simples o suficiente saber que algumas malhas terão vértices que têm a mesma posição.

No MeshStudy.cs, substitua todo o código dentro da função DoAction() pelo seguinte:

 PullSimilarVertices(index, localPos); 

Vamos para a função PullSimilarVertices() e adicionar o seguinte:

 Vector3 targetVertexPos = vertices[index]; //1 List<int> relatedVertices = FindRelatedVertices(targetVertexPos, false); //2 foreach (int i in relatedVertices) //3 { vertices[i] = newPos; } cMesh.vertices = vertices; //4 cMesh.RecalculateNormals(); 

Código Explicação:

  1. obtemos a posição do vértice de destino, que será usado como argumento para o método FindRelatedVertices() .
  2. Este método retorna uma lista de índices (correspondentes aos vértices) que têm a mesma posição que o vértice de destino.
  3. O loop percorre a lista inteira e define os vértices correspondentes como newPos .
  4. cMesh.vertices vertices atualizados de volta a cMesh.vertices . Em seguida, chamamos RecalculateNormals() para redesenhar a malha com os novos valores.

Salve o arquivo e retorne ao Unity. Arraste qualquer um dos vértices; agora a malha deve manter sua forma e não entrar em colapso.


Agora que completamos o primeiro passo na manipulação das malhas, salve a cena e passe para a próxima seção.

Manipulação de malha


Nesta seção, você aprenderá sobre a manipulação de malhas em tempo real. Existem várias maneiras, mas neste tutorial, examinaremos o tipo mais simples de manipulação de malha, ou seja, mover os vértices de malha criados anteriormente.

Coletando índices selecionados


Vamos começar selecionando os vértices que iremos mover em tempo real.

Cena aberta 02 Crie malha de coração na pasta Cenas . Na janela Cena, você verá uma esfera vermelha. Selecione Esfera na hierarquia e vá para o Inspetor . Você verá que o componente de script Heart Mesh está anexado ao objeto.

Agora precisamos do script Editor para esse objeto para exibir os vértices da malha na janela Cena. Vá para a pasta Editor e clique duas vezes em HeartMeshInspector.cs .

Na função ShowHandle() , dentro do bloco if , adicione o seguinte:

 Handles.color = Color.blue; if (Handles.Button(point, handleRotation, mesh.pickSize, mesh.pickSize, Handles.DotHandleCap)) //1 { mesh.selectedIndices.Add(index); //2 } 

Código Explicação:

  1. Define e exibe os vértices da malha como um tipo Handles.Button .
  2. Quando clicado, ele adiciona o índice selecionado à lista pressionada, mesh.selectedIndices .

No OnInspectorGUI() , antes do colchete de fechamento, adicione o seguinte:

 if (GUILayout.Button("Clear Selected Vertices")) { mesh.ClearAllData(); } 

Explicação do código: é assim que adicionamos um botão Redefinir ao Inspetor para chamar mesh.ClearAllData() .

Salve o arquivo e abra HeartMesh.cs na pasta Scripts . Na função ClearAllData() , adicione o seguinte:

 selectedIndices = new List<int>(); targetIndex = 0; targetVertex = Vector3.zero; 

Código Explicação: O código limpa os valores em selectedIndices e targetIndex . Ele também redefine o targetVertex .

Salve o arquivo e retorne ao Unity. Selecione Sphere e vá para o Inspector para o componente de script HeartMesh . Expanda Índices Selecionados clicando no ícone de seta ao lado. Isso nos permitirá rastrear cada vértice adicionado à lista.

Ative o Modo de edição usando a caixa de seleção ao lado. Devido a isso, os vértices da malha serão desenhados na janela Cena. Clicar nos pontos azuis nos índices selecionados deve alterar os valores de acordo. Teste também o botão Limpar vértices selecionados para garantir que ele apague todos os valores.


Nota: no Inspetor personalizado modificado, temos a opção de mostrar / ocultar o manipulador de transformação usando Mostrar identificador de transformação . Portanto, não entre em pânico se você não encontrar o manipulador Transform em outras cenas! Ligue-o antes de sair.

Transformando uma esfera em um coração


A alteração dos vértices da malha em tempo real consiste essencialmente em três etapas:

  1. Copie os vértices de malha atuais (antes da animação) para mVertices .
  2. mVertices cálculos e mVertices os valores em mVertices .
  3. Atualize os vértices de malha atuais com o mVertices ao mudar a cada etapa e deixe o Unity calcular automaticamente os normais.

Abra HeartMesh.cs e as seguintes variáveis ​​antes da função Start() :

 public float radiusofeffect = 0.3f; //1 public float pullvalue = 0.3f; //2 public float duration = 1.2f; //3 int currentIndex = 0; //4 bool isAnimate = false; float starttime = 0f; float runtime = 0f; 

Código Explicação:

  1. O raio da área afetada pelo vértice de destino.
  2. Arraste a força.
  3. A duração da animação.
  4. O índice atual da lista selectedIndices .

Na função Init() , antes do bloco if , adicione o seguinte:

 currentIndex = 0; 

Explicação do código: no início do jogo, currentIndex como 0, o primeiro índice da lista selectedIndices .

Na mesma função Init() , antes do colchete de fechamento do bloco else , adicione o seguinte:

 StartDisplacement(); 

Explicação do código: execute a função StartDisplacement() se isEditMode for false.

Dentro da função StartDisplacement() , adicione o seguinte:

 targetVertex = oVertices[selectedIndices[currentIndex]]; //1 starttime = Time.time; //2 isAnimate = true; 

Código Explicação:

  1. Selecione targetVertex para iniciar a animação.
  2. Defina a hora de início e altere o valor de isAnimate para true.

Após a função StartDisplacement() , crie a função FixedUpdate() com o seguinte código:

 void FixedUpdate() //1 { if (!isAnimate) //2 { return; } runtime = Time.time - starttime; //3 if (runtime < duration) //4 { Vector3 targetVertexPos = oFilter.transform.InverseTransformPoint(targetVertex); DisplaceVertices(targetVertexPos, pullvalue, radiusofeffect); } else //5 { currentIndex++; if (currentIndex < selectedIndices.Count) //6 { StartDisplacement(); } else //7 { oMesh = GetComponent<MeshFilter>().mesh; isAnimate = false; isMeshReady = true; } } } 

Código Explicação:

  1. A função FixedUpdate() é executada em um loop FPS fixo.
  2. Se isAnimate for falso, pule o código a seguir.
  3. Alterar animações de runtime .
  4. Se o runtime estiver dentro da duration , obteremos as coordenadas mundiais de targetVertex e DisplaceVertices() , cobrindo o vértice de destino com os radiusofeffect e radiusofeffect .
  5. Caso contrário, o tempo acabou. Adicione um ao currentIndex .
  6. Verifique se currentIndex entre os selectedIndices . Vá para o próximo vértice da lista usando StartDisplacement() .
  7. Caso contrário, no final da lista, altere os dados oMesh para a malha atual e isAnimate como false para interromper a animação.

Em DisplaceVertices() adicione o seguinte:

 Vector3 currentVertexPos = Vector3.zero; float sqrRadius = radius * radius; //1 for (int i = 0; i < mVertices.Length; i++) //2 { currentVertexPos = mVertices[i]; float sqrMagnitute = (currentVertexPos - targetVertexPos).sqrMagnitude; //3 if (sqrMagnitute > sqrRadius) { continue; //4 } float distance = Mathf.Sqrt(sqrMagnitute); //5 float falloff = GaussFalloff(distance, radius); Vector3 translate = (currentVertexPos * force) * falloff; //6 translate.z = 0f; Quaternion rotation = Quaternion.Euler(translate); Matrix4x4 m = Matrix4x4.TRS(translate, rotation, Vector3.one); mVertices[i] = m.MultiplyPoint3x4(currentVertexPos); } oMesh.vertices = mVertices; //7 oMesh.RecalculateNormals(); 

Código Explicação:

  1. O quadrado do raio.
  2. Passamos por cada vértice da malha.
  3. sqrMagnitude entre currentVertexPos e targetVertexPos .
  4. Se sqrMagnitude exceder sqrRadius , vá para o próximo vértice.
  5. Caso contrário, continue definindo o valor de falloff , que depende da distance vértice atual do ponto central do escopo.
  6. Vector3 nova posição Vector3 e aplique sua transformação ao vértice atual.
  7. Quando você sai do loop, atribuímos os valores alterados do mVertices aos mVertices do mVertices e forçamos o Unity a recalcular os valores normais.

Fonte de Tecnologia Falloff
A fórmula original é obtida no arquivo do pacote de recursos de Exemplos de procedimentos , que pode ser baixado gratuitamente no Unity Asset Store.

Salve o arquivo e retorne ao Unity. Selecione Esfera , acesse o componente HeartMesh e tente adicionar alguns vértices à propriedade Índices Selecionados . Desative o modo É de edição e clique em Reproduzir para ver o resultado do seu trabalho.


Experimente os valores Radiusofeffect , Pullvalue e Duration para obter resultados diferentes. Quando estiver pronto, altere as configurações de acordo com a captura de tela abaixo.


Clique em Play . Sua esfera se transformou em um coração?


Parabéns! Na próxima seção, salvaremos a malha como uma pré-fabricada para uso futuro.

Salvando a Malha em Tempo Real


Para salvar uma malha procedural em forma de coração no modo Reproduzir, você precisa preparar uma pré-fabricada cujo filho será um objeto 3D e, em seguida, substituir seu ativo de malha por um novo usando um script.

Na janela Projeto, localize CustomHeart na pasta Prefabs . Clique no ícone de seta para expandir seu conteúdo e selecione Filho . Agora você vê um objeto Sphere na janela de visualização do Inspetor . Essa é a pré-fabricada que armazenará os dados para a nova malha.


Abra HeartMeshInspector.cs . Dentro da função OnInspectorGUI() , antes do colchete de fechamento, adicione o seguinte:

 if (!mesh.isEditMode && mesh.isMeshReady) { string path = "Assets/Prefabs/CustomHeart.prefab"; //1 if (GUILayout.Button("Save Mesh")) { mesh.isMeshReady = false; Object pfObj = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)); //2 Object pfRef = AssetDatabase.LoadAssetAtPath (path, typeof(GameObject)); GameObject gameObj = (GameObject)PrefabUtility.InstantiatePrefab(pfObj); Mesh pfMesh = (Mesh)AssetDatabase.LoadAssetAtPath(path, typeof(Mesh)); //3 if (!pfMesh) { pfMesh = new Mesh(); } else { pfMesh.Clear(); } pfMesh = mesh.SaveMesh(); //4 AssetDatabase.AddObjectToAsset(pfMesh, path); gameObj.GetComponentInChildren<MeshFilter>().mesh = pfMesh; //5 PrefabUtility.ReplacePrefab(gameObj, pfRef, ReplacePrefabOptions.Default); //6 Object.DestroyImmediate(gameObj); //7 } } 

Código Explicação:

  1. Define o path para o caminho do objeto pré-fabricado CustomHeart.
  2. Cria dois objetos da prefab CustomHeart, um para criar uma instância como pfObj ( pfObj ) e o segundo como links ( pfRef ).
  3. Cria uma instância do pfMesh malha pfMesh . Se não for encontrado, cria uma nova malha, caso contrário, limpa os dados existentes.
  4. pfMesh com novos dados de malha e os adiciona como um ativo ao CustomHeart .
  5. Preenche um ativo de malha no gameObj valor pfMesh .
  6. Substitui CustomHeart por gameObj combinando conexões pré-existentes.
  7. Destrói instantaneamente o gameObj .

Salve o arquivo e vá para HeartMesh.cs . No método geral SaveMesh() , após criar a instância nMesh adicione o seguinte:

 nMesh.name = "HeartMesh"; nMesh.vertices = oMesh.vertices; nMesh.triangles = oMesh.triangles; nMesh.normals = oMesh.normals; 

Código Explicação: Retorna um ativo de malha com valores de uma malha em forma de coração.

Salve o arquivo e retorne ao Unity. Clique em Play . Após a animação ser concluída, o botão Salvar malha será exibido no Inspetor . Clique no botão para salvar a nova malha e pare o player.

Vá para a pasta Prefabs e consulte a pré-fabricada CustomHeart. Você deve ver que agora no objeto pré-fabricado CustomHeart há uma malha em forma de coração completamente nova.


Bom trabalho!

Juntando tudo


Na cena anterior, a função DisplaceVertices() usou a fórmula de Falloff para determinar a força de arrasto aplicada a cada vértice dentro de um determinado raio. O ponto de "queda", no qual a força de arrasto começa a diminuir, depende do tipo de Falloff usado: Linear, Gaussiano ou Agulha. Cada tipo produz resultados diferentes na malha.


Nesta seção, veremos outra maneira de manipular vértices: usando uma determinada curva. Tomando a regra de que a velocidade é igual à distância dividida pelo tempo (d = (v / t)), podemos determinar a posição do vetor, referindo-se à sua distância dividida pelo tempo.


Usando o método Curve


Salve a cena atual e abra 03 Customize Heart Mesh na pasta Scenes . Você verá uma instância de Hierarquia da pré-fabricada CustomHeart. Clique no ícone de seta ao lado dele para expandir seu conteúdo e selecione Filho .

Veja suas propriedades no Inspetor .Você verá o componente Filtro de malha com o ativo de malha do coração . Anexe um script de coração personalizado ao filho como um componente . Agora, o ativo deve mudar de HeartMesh para clonar .


Em seguida, abra CustomHeart.cs na pasta Scripts . Antes da função, Start()adicione o seguinte:

 public enum CurveType { Curve1, Curve2 } public CurveType curveType; Curve curve; 

Explicação do código: aqui é criada uma enumeração geral sob o nome CurveType, após a qual é disponibilizada pelo Inspetor .

Vá para CurveType1()e adicione o seguinte:

 Vector3[] curvepoints = new Vector3[3]; //1 curvepoints[0] = new Vector3(0, 1, 0); curvepoints[1] = new Vector3(0.5f, 0.5f, 0); curvepoints[2] = new Vector3(1, 0, 0); curve = new Curve(curvepoints[0], curvepoints[1], curvepoints[2], false); //2 

Código Explicação:

  1. Uma curva simples consiste em três pontos. Defina os pontos para a primeira curva.
  2. Geramos a primeira curva com a ajuda Curve()e atribuímos seus valores curve. A curva desenhada pode ser exibida na visualização se você especificar true como o último parâmetro.

Vá para CurveType2()e adicione o seguinte:

 Vector3[] curvepoints = new Vector3[3]; //1 curvepoints[0] = new Vector3(0, 0, 0); curvepoints[1] = new Vector3(0.5f, 1, 0); curvepoints[2] = new Vector3(1, 0, 0); curve = new Curve(curvepoints[0], curvepoints[1], curvepoints[2], false); //2 

Código Explicação:

  1. Defina os pontos para a segunda curva.
  2. Geramos a segunda curva Curve()e atribuímos seus valores curve. A curva desenhada pode ser exibida na visualização se você especificar true como o último parâmetro.

B StartDisplacement(), antes do colchete de fechamento, adicione o seguinte:

 if (curveType == CurveType.Curve1) { CurveType1(); } else if (curveType == CurveType.Curve2) { CurveType2(); } 

Explicação do código: aqui verificamos a opção selecionada pelo usuário curveTypee a geramos de acordo curve.

B DisplaceVertices(), dentro da instrução de loop forantes dos colchetes, adicione o seguinte:

 float increment = curve.GetPoint(distance).y * force; //1 Vector3 translate = (vert * increment) * Time.deltaTime; //2 Quaternion rotation = Quaternion.Euler(translate); Matrix4x4 m = Matrix4x4.TRS(translate, rotation, Vector3.one); mVertices[i] = m.MultiplyPoint3x4(mVertices[i]); 

Código Explicação:

  1. Nós obtemos a posição da curva em uma dada distancee multiplicamos seu valor ypor forcepara obter increment.
  2. Crie um novo tipo de dados Vector3para armazenar a nova posição do vértice atual e aplique sua transformação de acordo.

Salve o arquivo e retorne ao Unity. Verifique as propriedades do componente CustomHeart jogo de objetos Criança . Você verá uma lista suspensa onde pode selecionar o Tipo de curva . Na lista suspensa Editar tipo , selecione Adicionar índices ou Remover índices para atualizar a lista de vértices e experimentar configurações diferentes.


Para ver resultados detalhados para diferentes tipos de curvas, insira os valores de acordo com a captura de tela:


Para a lista Tipo de curva , selecione Curva1 , verifique se Nenhum está selecionado para Editar tipo e clique em Reproduzir . Você deve ver a malha divergir no padrão. Role o modelo para vê-lo em vista lateral e compare os resultados para os dois tipos de curvas. Aqui você vê como o tipo de curva selecionado afeta o deslocamento da malha.



Isso é tudo!Você pode clicar em Limpar vértices selecionados para redefinir os índices selecionados e experimentar seus próprios padrões. Mas não esqueça que existem outros fatores que afetarão o resultado final da malha, a saber:

  • O valor do raio.
  • A distribuição de vértices na área.
  • A posição do padrão dos vértices selecionados.
  • O método selecionado para o deslocamento.

Para onde ir a seguir?


Os arquivos do projeto finalizado estão no arquivo do projeto tutorial.

Não pare por aí! Experimente as técnicas mais sofisticadas usadas no tutorial Unity Procedural Maze Generation .

Espero que você tenha gostado deste tutorial e tenha achado as informações úteis. Um agradecimento especial eu expresso Jasper Flick of Catlike Codificação por suas excelentes tutoriais que me ajudaram a montar uma demo para o meu projeto.

Source: https://habr.com/ru/post/pt428796/


All Articles