No mecanismo do jogo Unity3D, uma sequência de código comum para obter dados remotos é a seguinte:
WWW www = new WWW("http://remote.com/data/location/with/texture.png");
yield return www;
Qual é o mecanismo subjacente aqui?
Sei que usamos o mecanismo de rendimento para permitir o processamento do próximo quadro, enquanto o download está sendo concluído. Mas o que está acontecendo sob o capô quando fazemos o yield return www
?
Qual método está sendo chamado (se houver, na classe WWW)? O Unity está usando threads? A camada "superior" do Unity está se apossando da instância www e está fazendo alguma coisa?
EDITAR:
- Esta pergunta é especificamente sobre os componentes internos do Unity3D. Não estou interessado em explicações sobre como a
yield
instrução funciona em c #. Em vez disso, estou procurando uma visão interna de como o Unity lida com essas construções, para permitir, por exemplo, à WWW baixar um pedaço de dados de maneira distribuída em vários quadros.
yield return
para operações assíncronas é um hack. Em um programa C # "real", você usaria umTask
para isso. O Unity provavelmente não os está usando porque foi criado antes do .Net 4.0, quandoTask
foi introduzido.Respostas:
Esta é a palavra-chave C # yield em ação - não está fazendo nada de especial com o
www
objeto, mas significa algo especial para o método em que está contido. Especificamente, essa palavra-chave pode ser usada apenas em um método que retorna umIEnumerable
(ouIEnumerator
) e é usado para indicar qual objeto será "retornado" pelo enumerador quando MoveNext for chamado.Funciona porque o compilador converte o método inteiro em uma classe separada que implementa
IEnumerable
(ouIEnumerator
) usando uma máquina de estado - o resultado líquido é que o corpo do método em si não é executado até que alguém enumere por meio do valor de retorno. Isso funcionará com qualquer tipo, não há absolutamente nada de especialWWW
, é o método que o contém, que é especial.Dê uma olhada nos bastidores da palavra-chave C # yield para obter mais informações sobre que tipo de código o compilador C # gera, ou apenas experimente e inspecione você mesmo usando algo como IL Spy
Atualização: para esclarecer
yield return
declaração, tudo o que acontece é que um enumerador é retornado - nenhum corpo do método é executado neste momentoMoveNext
o iterador para obter o primeiro valor na sequência. Isso faz com que o método execute até a primeirayeild return
instrução, momento em que o chamador é reiniciado (e presumivelmente o Unity continua a renderizar o restante do quadro)MoveNext
método no iterador uma vez a cada quadro subseqüente, fazendo com que o método seja executado novamente até a próximayield return
instrução uma vez a cada quadro, até que sejayield break
atingido o final do método ou de uma instrução (indicando final da sequência)O bit somente especial aqui (e em um par de outros casos ) é que a unidade não avança este iterador determinado o próximo quadro, em vez ela só avança o iterador (fazendo com que o método para continuar a execução) quando o download for concluído. Embora pareça haver uma classe YieldInstruction básica que, presumivelmente, contém um mecanismo genérico para sinalizar para o Unity quando um iterador deve ser avançado, a
WWW
classe não parece herdar dessa classe, portanto, posso apenas assumir que há um caso especial para essa classe no mecanismo do Unity.Só para esclarecer: a
yield
palavra - chave não faz nada de especial para aWWW
classe, é o tratamento especial que o Unity oferece aos membros da enumeração retornada que causa esse comportamento.Atualize o segundo: quanto ao mecanismo
WWW
usado para baixar páginas da Web de forma assíncrona, provavelmente ele usa o método HttpWebRequest.BeginGetResponse, que usará internamente a IO assíncrona ou, alternativamente, poderia usar threads (criando um thread dedicado ou usando um pool de threads).fonte
WWW
objeto quando é produzido, vejaWWW
a referência .yield
parece ser usado principalmente no Unity em um contexto de rotina. Para ler mais sobre as corotinas e por que elas usam C #,yield
recomendo este artigo do blog: Corotinas do Unity3D em detalhes . A maioria das pesquisas nesta resposta vem desse artigo.As corotinas no Unity são usadas para encapsular tarefas que:
Exemplos desses tipos de tarefas são (re) cálculos de localização de caminhos ou, como é o caso da sua pergunta, obter dados de um site.
Para responder às suas subquestões (em uma ordem ligeiramente modificada):
A
WWW
classe da Unity é projetada para ser produzida a partir de uma rotina. De acordo com os comentários no artigo do blog vinculado acima, o bloco de código especulativo (a camada "superior") sobreYieldInstruction
s na verdade contém uma opção que também verifica se háWWW
s produzidos . Esse código garante que a corotina termine automaticamente quando o download for concluído, conforme descrito naWWW
referência .Nesse caso, para baixar os dados "sem bloquear o resto do jogo": sim, muito provavelmente. (E o encadeamento é definitivamente usado para descompactar os dados baixados, conforme evidenciado por
WWW.threadPriority
.)fonte
Infelizmente, o WWW é implementado internamente como código nativo, o que significa que não podemos ver o código. Por experimentação, posso dizer que
WWW
não é derivadoYieldInstruction
, portanto, aconteça o que acontecer quando vocêyield
precisar ser tratado pelo código de caso especial.Eu nunca observei nenhuma diferença entre
e
Eu acho que é a maneira mais lógica de implementá-lo e, provavelmente, é o que está acontecendo sob o capô. Mas não tenho certeza.
O Unity não inicia um novo segmento para download, pelo menos em algumas plataformas (iOS, webplayer). Ou, se o fizer, ele define
WWW.isDone
no thread principal. Eu sei disso porque este código:não funciona
Eu não acho que você possa ter respostas mais específicas, a menos que alguém com acesso ao código fonte do Unity3d venha aqui.
fonte
Como o Unity3D usa o C # como seu mecanismo de script, suponho que seja a palavra-chave yield padrão que está embutida no C #. Basicamente, o que isso significa é que ele já retorna o valor de www para que você possa continuar enquanto a próxima iteração retornará o próximo valor, etc ... O rendimento cria basicamente uma máquina de estado e um iterador em segundo plano.
fonte
WWW
a referência . Além disso, não tenho certeza seyield
cria alguma coisa. O contexto do iterador é criado implementandoIEnumerable
ou usando-o como um tipo de retorno. "Máquina de estado" parece desligada também. Claro, existe estado, mas isso por si só não é uma propriedade suficiente, certo? Talvez você possa elaborar sobre isso.