Eu tenho um algoritmo de geração de nível que é computacionalmente pesado. Como tal, chamá-lo sempre resulta no congelamento da tela do jogo. Como colocar a função em um segundo segmento enquanto o jogo ainda continua renderizando uma tela de carregamento para indicar que o jogo não está congelado?
unity
multithreading
DarkDestry
fonte
fonte
List
de Ações para armazenar as funções que você deseja chamar no thread principal. bloqueie e copie aAction
lista naUpdate
função para a lista temporária, limpe a lista original e execute oAction
código naList
lista principal. Veja UnityThread do meu outro post sobre como fazer isso. Por exemplo, para chamar uma função no Thread principal,UnityThread.executeInUpdate(() => { transform.Rotate(new Vector3(0f, 90f, 0f)); });
Respostas:
Atualização: em 2018, o Unity está lançando um C # Job System como uma maneira de descarregar o trabalho e fazer uso de vários núcleos da CPU.
A resposta abaixo é anterior a este sistema. Ainda funcionará, mas pode haver melhores opções disponíveis no Unity moderno, dependendo de suas necessidades. Em particular, o sistema de tarefas parece resolver algumas das limitações sobre o que os threads criados manualmente podem acessar com segurança, descritos abaixo. Desenvolvedores que experimentam o relatório de visualização realizando raycasts e construindo malhas em paralelo , por exemplo.
Convido usuários com experiência no uso desse sistema de tarefas a adicionar suas próprias respostas, refletindo o estado atual do mecanismo.
Eu usei threading para tarefas pesadas no Unity no passado (geralmente processamento de imagem e geometria), e não é drasticamente diferente do que usar threads em outros aplicativos C #, com duas ressalvas:
Como o Unity usa um subconjunto um pouco mais antigo do .NET, existem alguns recursos e bibliotecas de encadeamento mais recentes que não podemos usar imediatamente, mas o básico está lá.
Como Almo observa em um comentário acima, muitos tipos de Unity não são seguros para threads e lançarão exceções se você tentar construí-las, usá-las ou mesmo compará-las na thread principal. Coisas para manter em mente:
Um caso comum é verificar se uma referência GameObject ou Monobehaviour é nula antes de tentar acessar seus membros.
myUnityObject == null
chama um operador sobrecarregado para qualquer coisa descendente de UnityEngine.Object, masSystem.Object.ReferenceEquals()
trabalha em torno disso até certo ponto - lembre-se de que um GameObject de Destroy () ed se compara como igual a nulo usando a sobrecarga, mas ainda não é ReferenceEqual como nulo.A leitura de parâmetros dos tipos Unity geralmente é segura em outro segmento (pois não gera uma exceção imediatamente, desde que você tenha cuidado em verificar nulos como acima), mas observe o aviso de Philipp aqui de que o segmento principal pode estar modificando o estado enquanto você está lendo. Você precisará ser disciplinado sobre quem tem permissão para modificar o quê e quando, a fim de evitar a leitura de um estado inconsistente, o que pode levar a erros que podem ser incrivelmente difíceis de rastrear, porque dependem de tempos de menos de um milissegundo entre os threads que podemos reproduza à vontade.
Membros estáticos Random e Time não estão disponíveis. Crie uma instância de System.Random por thread, se você precisar de aleatoriedade, e System.Diagnostics.Stopwatch, se precisar de informações de tempo.
As funções Mathf, Vector, Matrix, Quaternion e Color structs funcionam bem em threads, para que você possa executar a maioria dos seus cálculos separadamente
Criar GameObjects, anexar Monobehaviours ou criar / atualizar Texturas, Malhas, Materiais, etc., tudo precisa acontecer no thread principal. No passado, quando eu precisava trabalhar com isso, configurei uma fila produtor-consumidor, na qual meu thread de trabalho prepara os dados brutos (como uma grande variedade de vetores / cores para aplicar a uma malha ou textura), e uma Atualização ou Coroutine no thread principal pesquisa dados e os aplica.
Com essas anotações fora do caminho, eis um padrão que frequentemente uso para trabalhos encadeados. Não garanto que seja um estilo de práticas recomendadas, mas ele faz o trabalho. (Comentários ou edições para melhorar são bem-vindos - eu sei que o encadeamento é um tópico muito profundo, do qual eu só sei o básico)
Se você não precisa estritamente dividir o trabalho entre threads para obter velocidade, e está apenas procurando uma maneira de torná-lo sem bloqueio, para que o resto do seu jogo continue correndo, uma solução mais leve no Unity é a Coroutines . Essas são funções que podem fazer algum trabalho e, em seguida, devolvem o controle ao mecanismo para continuar o que está fazendo e retomar sem interrupções posteriormente.
Isso não precisa de considerações especiais de limpeza, pois o mecanismo (até onde eu sei) se livra das corotinas dos objetos destruídos para você.
Todo o estado local do método é preservado quando ele produz e continua, portanto, para muitos propósitos, é como se estivesse sendo executado ininterruptamente em outro encadeamento (mas você tem todas as conveniências de executar no encadeamento principal). Você só precisa garantir que cada iteração seja curta o suficiente para que não diminua seu encadeamento principal de maneira irracional.
Ao garantir que operações importantes não sejam separadas por um rendimento, você pode obter a consistência do comportamento de thread único - sabendo que nenhum outro script ou sistema no thread principal pode modificar os dados nos quais você está trabalhando.
A linha de retorno de rendimento oferece algumas opções. Você pode...
yield return null
para retomar após a atualização do próximo quadro ()yield return new WaitForFixedUpdate()
para retomar após o próximo FixedUpdate ()yield return new WaitForSeconds(delay)
para retomar após um certo tempo decorridoyield return new WaitForEndOfFrame()
para retomar após a GUI terminar a renderizaçãoyield return myRequest
ondemyRequest
é uma instância da WWW , para continuar assim que os dados solicitados terminarem o carregamento da Web ou do disco.yield return otherCoroutine
ondeotherCoroutine
é uma instância da Coroutine , para retomar após aotherCoroutine
conclusão. Isso geralmente é usado no formatoyield return StartCoroutine(OtherCoroutineMethod())
para encadear a execução a uma nova corotina que pode render quando ela deseja.Experimentalmente, pular o segundo
StartCoroutine
e simplesmente escreveryield return OtherCoroutineMethod()
realiza o mesmo objetivo se você deseja encadear a execução no mesmo contexto.O agrupamento dentro de a
StartCoroutine
ainda pode ser útil se você desejar executar a corotina aninhada em associação com um segundo objeto, comoyield return otherObject.StartCoroutine(OtherObjectsCoroutineMethod())
... dependendo de quando você deseja que a corotina faça a próxima curva.
Ou
yield break;
para interromper a corotina antes que ela chegue ao fim, da maneira que você pode usarreturn;
para sair mais cedo de um método convencional.fonte
Você pode colocar seu cálculo pesado em outro thread, mas a API do Unity não é segura para threads, você deve executá-los no thread principal.
Bem, você pode experimentar este pacote na Asset Store para ajudá-lo a usar a segmentação mais facilmente. http://u3d.as/wQg Você pode simplesmente usar apenas uma linha de código para iniciar um thread e executar a API do Unity com segurança.
fonte
Usando o Svelto.Tasks, você pode retornar o resultado de uma rotina multithread para o thread principal (e, portanto, para funções de unidade) com bastante facilidade:
http://www.sebaslab.com/svelto-taskrunner-run-serial-and-parallel-asynchronous-tasks-in-unity3d/
fonte
@DMGregory explicou muito bem.
É possível usar rosqueamento e corotinas. Anterior para descarregar o encadeamento principal e posteriormente para retornar o controle para o encadeamento principal. Pressionar o trabalho pesado para separar a linha parece mais razoável. Portanto, as filas de tarefas podem ser o que você procura.
Há realmente um bom exemplo de script do JobQueue no Unity Wiki.
fonte