Eu sou novo no Unity. Eu estava aprendendo corotinas e escrevi isso.
private void Fire()
{
if(Input.GetButtonDown("Fire1"))
{
StartCoroutine(FireContinuously());
}
if(Input.GetButtonUp("Fire1"))
{
StopAllCoroutines();
}
}
IEnumerator FireContinuously()
{
while(true)
{
GameObject laser = Instantiate(LaserPrefab, transform.position, Quaternion.identity) as GameObject;
laser.GetComponent<Rigidbody2D>().velocity = new Vector2(0, 10f);
yield return new WaitForSeconds(firetime);
}
}
Quando o botão é pressionado, a corotina é chamada e entra no loop 'while'. Quando deixo o botão, ele interrompe a corotina. Não deveria ficar preso no loop 'while', pois é um loop infinito? Por quê?
unity
c#
coroutines
babybrain
fonte
fonte
"Fire1"
, é algo que você pode configurar no mecanismo para permitir remapeamentos importantes em vez de digitarKeycode.Foo
?yield
útil perceber que é efetivamente a abreviação de "Rendimento do controle para o chamador até que o próximo item no Enumerável seja solicitado".StopAllCoroutines()
neste caso. Tudo bem quando você está apenas usando uma corotina, mas se você planeja ter mais de uma, isso terá efeitos indesejados. Em vez disso, você deve usarStopCoroutine()
e parar o que é relevante em vez de todos eles. (StopAllCoroutines()
Seria útil, por exemplo, quando terminar o nível ou o carregamento de uma nova área, etc., mas não para o material específico, como "Eu não estou disparando mais.")Respostas:
O motivo é a palavra-chave
yield
que possui um significado específico em C #.Ao encontrar as palavras,
yield return
uma função em C # retorna, como seria de esperar.Portanto, não há loop infinito. Existe uma função / iterador que pode ser chamado um número infinito de vezes.
A função Unity
StartCoroutine()
faz com que a estrutura do Unity chame a função / iterador uma vez por quadro.A função Unity
StopAllCoroutines
faz com que a estrutura do Unity pare de chamar a função / iterador.E retornar
WaitForSeconds(time)
do iterador faz com que a estrutura do Unity suspenda a chamada da função / iteradortime
.Um comentário confuso e um voto positivo igualmente confuso sobre esse comentário me incentivaram a aprofundar o que a palavra-chave
yield
faz e não faz.Se você escrever isso:
Você também pode escrever o seguinte:
Daqui resulta que a palavra
yield
- chave não está relacionada com multiencadeamento e absolutamente não é chamadaSystem.Threading.Thread.Yield()
.fonte
On encountering the words yield return a function in C# returns
" Não, não tem. O texto que você cita explica, assim como a Wikipedia - "In computer science, yield is an action that occurs in a computer program during multithreading, of forcing a processor to relinquish control of the current running thread, and sending it to the end of the running queue, of the same scheduling priority.
". Basicamente, "` por favor, faça uma pausa onde estou e deixe outra pessoa correr por um tempo ".Quando o botão de disparo é pressionado, o segundo se a instrução é inserida e o StopAllCoroutines é executado. Isso significa que a Corotina na qual o loop while está sendo finalizado termina, portanto, não há mais loop infinito. A corotina é como um contêiner para o código executar.
Posso recomendar o Manual do Unity e a API de script do Unity para entender melhor o que são as corotinas e o quão poderosas elas podem ser.
Este blog e a postagem no YouTube também foram úteis para eu usar melhor as corotinas.
fonte
As corotinas são um animal estranho. O retorno de rendimento faz com que o método suspenda a execução até que seja posteriormente escalado. Nos bastidores, pode ser algo como isto:
E interno ao Unity / C # (como o retorno de rendimento é um recurso nativo de c #), quando você chama StartCoroutine, ele cria um
FireContinuouslyData
objeto e o transmite ao método. Com base no valor de retorno, ele determina quando chamá-lo novamente mais tarde, simplesmente armazenando o objeto FireContinuouslyData para transmiti-lo na próxima vez.Se você alguma vez quebrasse o rendimento, ele poderia ser definido internamente
data.shouldBreak = true
e o Unity simplesmente descartaria os dados e não os agendaria novamente.E se houvesse algum dado que precisasse ser salvo entre as execuções, ele também seria armazenado nos dados para mais tarde.
Um exemplo de como o Unity / C # pode implementar a funcionalidade da rotina:
fonte
Outra resposta menciona que você está interrompendo as co-rotinas quando
"Fire1"
terminar - isso é completamente correto, pois a corotina não continua instanciando GameObjects após o primeiro pressionamento de"Fire1"
.No seu caso, no entanto, esse código não ficará "preso" em um loop infinito, que é o que parece que você está procurando uma resposta - ou seja, o
while(true) {}
loop, mesmo que você não o tenha parado externamente.Ele não fica preso, mas a sua rotina não termina (sem ligar
StopCoroutine()
ouStopAllCoroutines()
). Isso ocorre porque as corotinas do Unity cedem controle ao chamador.yield
ing é diferente dereturning
:return
instrução interromperá a execução de uma função, mesmo se houver mais código após elayield
instrução pausará a função, iniciando na próxima linha depois deyield
retomada.Normalmente, as corotinas serão retomadas a cada quadro, mas você também retornará um
WaitForSeconds
objeto.A linha
yield return new WaitForSeconds(fireTime)
se traduz aproximadamente como "agora me suspenda e não volte até que osfireTime
segundos se passem".A menos que parado, esta é uma rotina que, uma vez iniciada, executará todo o loop a cada
fireTime
segundo.fonte
Uma explicação simples: sob o capô, o Unity está interagindo sobre uma coleção (de YieldInstruction s ou nulls ou o que você quiser
yield return
) usando oIEnumerator
que sua função retorna.Como você usa a
yield
palavra - chave, seu método é um iterador . Não é a coisa do Unity, é um recurso da linguagem C #. Como funciona?É preguiçoso e não gera toda a coleção de uma só vez (e a coleção pode ser infinita e impossível de ser gerada ao mesmo tempo). Os elementos da coleção são gerados conforme necessário. Sua função retorna um iterador para o Unity trabalhar. Ele chama seu
MoveNext
método para gerar um novo elemento eCurrent
propriedade para acessá-lo.Portanto, seu loop não é infinito, ele executa algum código, retorna um elemento e retorna o controle de volta ao Unity, para que não fique preso e possa fazer outro trabalho, como manipular sua entrada para interromper a corotina.
fonte
Pense em como
foreach
funciona:O controle sobre a iteração está no chamador - se você parar a iteração (aqui com
break
), é isso.A
yield
palavra-chave é uma maneira simples de tornar um enumerável em C #. O nome sugere isso -yield return
retorna o controle ao chamador (nesse caso, nossoforeach
); é o chamador que decide quando continuar para o próximo item. Então você pode criar um método como este:Parece ingênuo que funcionará para sempre; mas, na realidade, depende inteiramente do chamador. Você pode fazer algo assim:
Isso pode ser um pouco confuso se você não estiver acostumado a esse conceito, mas espero que também seja óbvio que essa é uma propriedade muito útil. Era a maneira mais simples de fornecer controle ao seu interlocutor e, quando o interlocutor decide fazer o acompanhamento, ele pode executar o próximo passo (se o Unity fosse feito hoje, provavelmente seria usado em
await
vez deyield
; masawait
não existia de volta). então).Tudo o que você precisa para implementar suas próprias rotinas (desnecessário dizer, as rotinas mais simples e estúpidas) é o seguinte:
Para adicionar uma
WaitForSeconds
implementação muito simples , você só precisa de algo como isto:E o código correspondente em nosso loop principal:
Ta-da - é tudo o que um sistema simples de rotina precisa. E, cedendo controle ao chamador, ele pode decidir sobre qualquer número de coisas; eles podem ter uma tabela de eventos classificados em vez de iterar por todas as corotinas em todos os quadros; eles podem ter prioridades ou dependências. Permite a implementação muito simples de multitarefa cooperativa. E veja como isso é simples, graças a
yield
:)fonte