2012/6/5 George Lucas <
geoc...@gmail.com>:
> O problema está exatamente aí: a única forma encontrada de instanciar o
> player no grid assim é no evento OnMouseDown() do script ligado aos tiles -
> assim é fácil pegar onde o jogador clicou e saber onde deve-se instanciar um
> boneco. A questão é que não basta pegar a instância do script ligado ao
> controlador e deixar um pedaço dele em loop até atualizar uma certa variavel
> nele (via um setter), porque assim o Unity trava e impossibilita qualquer
> análise. Creio que a solução possa estar na utilização do IEnumerator e das
> Coroutines, mas tá difícil lidar com esses iteradores - to tentando chamar
> uma função gambiarra() pela StartCoroutine() q só dá um yield return 1 ou
> yield return 0 dependendo se o valor da variavel (posição) em questão é
> (0,0,0) ou se foi atualizada pelo outro script, mas ainda assim o Unity
> continua travando.
Uma corotina não pode "retornar" um valor no sentido normal da
palavra. Ela só retorna um valor especial que indica quando o fluxo de
execução deve retornar a ela. Então você pode fazer, por exemplo...
yield return null -- volta a executar no próximo frame
yield return new WaitForEndOfFrame() -- volta a executar no fim do
frame atual (junto com os LateUpdates)
yield return new WaitForSeconds(x) -- volta a executar daqui a x segundos
Sendo que ela vai voltar a execução da linha seguinte à linha do yield.
De qualquer forma, não parece que o que você quer fazer deve usar
isso. Corotinas em geral são boas para gerencias ações que levam mais
de um frame (por exemplo, o personagem andar de um tile a outro) ou
para atrasar algum tipo de processamento que não pode ser feito no
momento.
Me parece que a solução para o seu problema (sem mudar muito a idéia)
é simplesmente chamar um método
GameController.CriarJogadorEmTile(tile). Para isso você tem que ter
uma referência ao GO do controlador no script do tile e pegar o
componente GameController dele, ou então guardar de cara o
GameController.
Outro jeito de fazer, usando a idéia do controller ficar "esperando"
uma variável, é usar o Update(). Como ele é executado todo frame, você
pode fazer um if(tile_foi_clicado) { //fazer coisas } dentro do
Update. Como ele roda todo frame, comporta vários comportamentos do
tipo "verificar se algo aconteceu, e caso tenha acontecido, fazer
coisas".
> 2- Por que diabos a função Start() não é a primeira a entrar em ação sempre?
> Esse script aí de cima ligado ao controlador é chamado por outro também
> ligado ao controlador dessa forma:
>
> ------------------------------------------------------------------------------------------------------
> CriaPersonagem cp = GetComponent<CriaPersonagem>();
> players = cp.Run(width, height); //retorna uma lista dos
> players criados
> ------------------------------------------------------------------------------------------------------
>
> E botando um breakpoint na função Start() do CriaPersonagem e outro na
> função Run(), dá pra notar que a função Run() roda antes da função Start()!
> E como preciso de coisas na função Start() antes de executar o Run(), o
> Unity fica soltando NullReferenceExceptions. Tava achando que na chamada do
> GetComponent() o Unity trataria de chamar o Start() no script, mas pelo
> visto não é isso que ocorre - e o mais estranho é ele chamar o Start()
> depois do Run(), do nada, sem motivo aparente. Alguém já passou por isso?
O Start não é um construtor e não é chamado imediatamente quando algo
é instanciado ou acessado, mas sim durante o começo do primeiro frame
em que o GameObject existe na cena. Sendo assim, o código de um
Start() não pode depender de que o Start() de outro objeto tenha sido
chamado. Uma solução rápida para isso é colocar o comportamento do
objeto a ser inicializado antes no Awake(), que é chamado um pouco
antes do Start() para todos os objetos.
E pra ficar claro, o GetComponent não inicializa nada nem gera
qualquer tipo de ação! Ele apenas retorna uma referência para o objeto
(as in OOP, não GameObject) da classe especificada (no caso o
CriaPersonagem) que esteja funcionando como um componente para o
GameObject atual.
Então, o ciclo de criação e correspondência entre GOs, componentes e
afins quando um GO é criado é o mais ou menos seguinte:
- A unity "sabe" (por estruturas internas dela, geradas no editor) que
um certo GameObject, ou seja, um objeto na cena ou um Prefab, tem
atrelado a ele uma lista de componentes. Um componente nada mais é do
que uma instância de uma classe que deriva de Component (lembrando que
MonoBehavior, e logo todos os scripts que você faz por padrão, derivam
de Component também).
- Internamente, para cada componente que um GO tem, a Unity guarda
(serializado) o tipo desse componente (Transform, SphereCollider,
CriaPersonagem) e todos os parâmetros expostos no Inspector para
aquela classe (por padrão, todas as variáveis públicas, recursivamente
no caso do tipo de uma variável pública ser outra classe).
- Quando um GameObject é instanciado na cena (o que, IMPORTANTE, no
editor pode acontecer várias vezes em momentos aleatórios), as
seguintes coisas acontecem:
---> Estruturas internas da Unity são atualizadas para refletir que um
novo GameObject está na cena e uma instância da classe GameObject é
criada.
---> A lista serializada de componentes é lida (provavelmente da
memória, em algum lugar interno da engine). Para cada componente
registrado para aquele objeto, a classe correspondente é instanciada
(chamando o construtor da mesma) e suas variáveis públicas são setadas
de acordo com os valores serializados.
- Até agora tudo acontece imediatamente na hora em que o GO é
instanciado (por exemplo, com um Instantiate que pode ser no meio de
um frame ou durante o carregamento da cena). Observem que até agora
nada de Start, Awake ou qualquer outra coisa.
- No começo do próximo frame, SE NÃO ESTIVERMOS NO EDITOR, é chamado
Awake(), em todos os componentes que implementem esse método, nos
novos objetos (criados desde o frame anterior) e, em seguida
(intercalados com coisas da engine) é chamado Start() da mesma forma.
- Tanto no editor quanto em runtime, e sempre que houver uma
recompilação de scripts durante a execução do jogo, é chamado também o
OnEnable() da mesma forma em todos os componentes.
- Mais pro meio do frame, o primeiro Update() do novo objeto é chamado
"junto" (em uma ordem aleatória, na verdade) e da mesma forma que de
todos os outros GOs, e a vida segue normalmente.
--
Bruno "Tinnus"