Articles

GetComponent in C#

neste tutorial

  • acedendo a outro programa de dentro do objecto
  • Como funciona o GetComponent?obter muitos componentes interacção entre objectos.
  • Interaction between objects within the hierarchy
  • SendMessage and BroadcastMessage
  • interactive functions

Introduction

uma questão recorrente quando se começa com a unidade é como aceder aos Membros num programa a partir de outro programa. Muitos pensariam que a dereferenciação do nome do script seria suficiente, eles rapidamente percebem que não é.

ao desenvolver um programa, as variáveis são armazenadas na memória em diferentes locais. Se um objeto pudesse ver outros objetos membros, haveria um risco de modificá-los, mesmo que não fosse intencional. Se para instâncias dois objetos possuem o mesmo script com os mesmos nomes variáveis, o compilador não seria capaz de descobrir a qual estamos nos referindo.

para evitar este problema, cada objeto na memória não pode ver outros objetos. É então necessário dizer a um objeto onde a variável que ele precisa está na memória. Todo o princípio do ponteiro foi abandonado há muito tempo e agora estamos usando referência em vez disso (que na verdade usa ponteiro internamente).

Existem várias formas de acessar variáveis, algumas são melhores do que outras e algumas são simplesmente para serem usadas em situações particulares.

  1. GetComponent, o mais comum, também o que confunde mais no início
  2. SendMessage, pode parecer mais fácil de entender, mas também é menos eficiente.
  3. variáveis estáticas, as mais fáceis mas também as mais complicadas de entender completamente no início.

configurar uma cena

criar uma nova cena, adicionar um objecto de jogo vazio e nomeá-lo “StormTrooper”. Criar dois scripts chamados “Health”e ” Force”. Adiciona ambos os programas ao StormTrooper.

abra ambos os programas no editor e adicione estas linhas:

saúde.cs

using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private int health = 5; private bool hasForce = false; private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { this.hasForce = Force.ReportForce(); if(hasForce == true) { this.health = 10; } } }}

força.cs

using UnityEngine;using System.Collections;public class Force : MonoBehaviour{ private bool force = false; private void Start() { this.force = true; } private void Update() { if (Input.GetKeyDown(KeyCode.P)) { string str = (this.force == true) ? " the": " no"); Debug.Log("I have" + str + " force"); } } public bool ReportForce() { return force; }}

tentar este código irá simplesmente retornar erro”uma referência de objeto é necessária para acessar membros não-estáticos”.

precisamos dizer ao componente de Saúde no StormTrooper onde está o componente de força para que ele possa acessar seus membros. Uma vez que nossos scripts estão ambos no mesmo objeto nós simplesmente precisamos declarar uma variável do tipo do script que desejamos acessar e usar GetComponent para encontrar o script.

de Acessar outro script dentro do objeto

Modificar Saúde com:

using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private Force forceScript= null; public int health = 5; private bool hasForce = false; private void Start() { this.forceScript= GetComponent<Force>(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { this.hasForce = this.forceScript.ReportForce(); if(this.hasForce == true) { this.health = 10; } } }}

Com as modificações, execute o jogo, pressione a tecla de Espaço e, em seguida, prima P, o StormTrooper agora tem 10 da saúde (Note que um StormTrooper não tem a força, isso é só para o exemplo). Acedes às variáveis da mesma forma que acedeste aos métodos. Certifique-se apenas de que os membros que você está tentando alcançar são declarados públicos ou você vai obter um erro.se os membros fossem privados, não seria possível acessá-los. Tudo o que não deve ser alcançado fora da classe/script deve ser privado para lidar com o princípio de encapsulação. como funciona o GetComponent?

primeiro declaramos um objeto do tipo do script que queremos alcançar.

private Force forceScript = null;

força é o nosso tipo e forceScript é apenas um nome que fornecemos para usar a referência no script. Podemos dar-lhe qualquer nome.

A função GetComponent irá procurar dentro do objecto (o objecto do jogo StormTrooper) para encontrar um componente correspondente ao tipo que passamos. Se não for encontrada nenhuma referência nula é devolvida e nossa variável forceScript não fará nada. Se um componente do tipo de força é encontrado, então o endereço desse componente é passado para a variável forceScript, a variável agora aponta para o componente de força armazenado em algum lugar na memória. A fim de acessar os membros públicos da Força, nós só precisamos dereferenciar a variável de script usando o operador de ponto. Diz-se que a variável forceScript é uma pega para o objeto. Dá acesso a todos os membros públicos (e internos).

script.PublicMembers;

membros privados e protegidos permanecerão inacessíveis.

Usando o atributo SerializeField permite ver as variáveis no inspector despute sendo privadas. Sua variável forceScript começa com o valor de nenhum(Força) e se transforma em uma referência de força. Se você clicar nele, ele irá destacar o objeto alvo.

reference_component

So I can cache component with GetComponent?

Sim, e recomenda-se que o faça para alguns componentes usados regularmente.

Ao acessar componentes incorporados, tais como Rigidbody, Transformação, Processador, etc…, a Unidade usada para permitir a “direta” de acesso, como este:

private void Update(){ this.transform.position += this.transform.forward; this.rigidbody.AddForce(force);}

Eles removidos todos eles, mas a transformação, pois há sempre um Transformar em qualquer objeto do jogo.

GetComponent deve ser evitado dentro da atualização para que Cache é recomendado com algumas linhas simples de código como:

private Transform myTransform = null;private Rigidbody rigidbody = null;private void Start(){ this.myTransform = this.gameObject.GetComponent<Transform>(); this.rigidbody = this.gameObject.GetComponent<Rigidbody>();}private void Update(){ this.myTransform.position += this.myTransform.forward; this.rigidbody.AddForce(force);}

Isto irá salvar alguns cálculos.

recebendo muitos componentes

e se o nosso StormTrooper tem muita força dentro de si (embora não seja provável). Duplica a força duas vezes para o StormTrooper ter três.

Health becomes:

using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private Force scripts = null; private int health = 5; private void Start() { this.scripts = this.gameObject.GetComponents<Force>(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { for (int i = 0; i < this.scripts.Length; i++) { if(this.scripts.ReportForce()) { this.health += 5; } } } }}

execute o jogo, press Space, our StormTrooper is becoming a Jedi. GetComponents procura por todos os componentes do tipo e retorna um array deles. Isto é bom se você não alvo Um componente específico do tipo, mas mais provável todos eles.

ver o seguinte iria ligar e apagar as luzes:

public class LightFlicker:MonoBehaviour{ private Light lights = null; private float timer = 0.0f private float clockTimer = 0.0f; private void Start(){ this.lights = this.gameObject.GetComponents<Light>(); this.clockTimer = Random.Range(0.5f, 2.0f); } private void Update(){ this.timer += Time.deltaTime; if(this.timer > this.clockTimer){ this.timer = 0.0f; this.clocktimer = Random.Range(0.5f, 2.0f); for(int i = 0; i < this.lights.Length; i++){ lights.enabled = !lights.enabled; } } }}

interacção entre objectos.

também podemos obter dois objetos diferentes para interagir de uma forma semelhante. Crie um novo objeto chamado “Sith”. Remova os componentes duplicados do StormTrooper para que só reste um.

using UnityEngine;using System.Collections;public class DarkSide : MonoBehaviour { private const bool force = true; private Force script = null; private void Start () { this.script = GameObject.Find ("StormTrooper").GetComponent<Force>(); } private void Update () { if(Input.GetKeyDown (KeyCode.Space)) { UseForceToControl (); } } private void UseForceToControl() { if(this.script.ReportForce() == false) { Debug.Log ("Do as I say."); } }}

modifique o programa de força do StormTrooper para que ele não tenha a força (os Sith achariam isto suspeito…), dou-lhe uma pista que acontece no programa inicial.

abaixo está a linha que encontra o StormTrooper e obtém o seu componente de força.

private void Start () { this.script = GameObject.Find ("StormTrooper").GetComponent<Force>();}

GameObject.Find () first finds the object passed as parameter and then looks for the required component inside of it. Recomenda-se realizar essas ações no início ou em uma época do jogo onde os GAL não afetarão a jogabilidade. GameObject.Find and GetComponent are expensive functions since they need to iterate through the whole Hierarchy. Se a sua cena inclui apenas 10 objetos, será bom, mas se ela contém milhares deles (um exército de clones) você certamente verá um efeito ruim. Considere chamá-los na função de início. Note que se sua cena contém muitos StormTroopers, Uity não pode saber o que você queria usar e vai retornar o primeiro que encontrar. Muito provavelmente, você tem uma referência ao objeto com o qual deseja interagir, por exemplo, uma chamada de colisão.

interação entre objetos dentro da hierarquia

considere um veículo, ao conduzi-lo, o personagem Está ligado a ele como um objeto criança. O veículo teria uma característica especial se o condutor tivesse a força (melhor manuseamento, mais rápido e assim por diante). Como o motorista Está ligado, ele está abaixo na hierarquia.
O veículo pode encontrar o componente de força do condutor com o mesmo princípio que usamos anteriormente (GameObject.Encontrar) mas podemos fazê-lo acontecer mais rápido, indicando ao veículo que o condutor está na sua própria hierarquia.

private Force script = null;private void Start () { this.script = this.transform.Find ("Sith").GetComponent<DarkSideForce>();}

GameObject é trocado por transformada. Na verdade, um objeto criança pertence à transformação do pai, então precisamos olhar para a transformação para encontrar o nosso objeto. No exemplo, o objeto está bem sob o objeto chamador, se sua Sith está dentro do objeto cockpit sob o objeto do carro, você precisa fornecer o caminho:

private void Start () { this.script = this.transform.Find ("Cockpit/Sith").GetComponent<DarkSideForce>();}

o objeto chamador é o carro e é referido como este Na chamada. Em seguida, o Cockpit é revistado e Sith é revistado. Se o caminho estiver errado, Find retorna nulo e você recebe uma exceção de referência nula. Você pode evitar que assim:

private void Start () { Transform tr = this.transform.Find ("Cockpit/Sith"); if(tr != null){ this.script = tr.GetComponent<DarkSideForce>(); }else{Debug.LogWarning("No component found");}}

transformar.O Find devolve uma transformação ao mesmo tempo que o GameObject.O Find devolve um GameObject.

ainda melhor, podemos usar:

private void Start () { this.script = GetComponentInChildren<DarkSideForce>();}

Agora nosso veículo pode saber que nossa Sith tem a força e aplicar característica especial.
Uma questão para saber, se o objeto pai contém um script do tipo que você está procurando, ele irá devolvê-lo. Então, pensando que você está lidando com o componente infantil, você está lidando com o pai.

neste exemplo, precisamos verificar para cada tipo de condutor se é um StormTrooper ou um Sith. Uma melhor abordagem teria sido usar a herança para que a classe de base superior iria manter a variável de força e uma única função seria usada para todo o tipo de driver. Mas herança e eficiência não eram a nossa principal questão aqui.

SendMessage e BroadcastMessage

SendMessage e BroadcastMeassage são outros dois possibilties que pode ser mais simples, mas também mais caro. O princípio permanece o mesmo, temos uma referência a um objeto e queremos modificar um script que ele contém.

com SendMessage, você pode chamar uma função em um objeto sem ter que encontrar uma referência ao script. Agora você me diz: “O que é todo esse passeio da confusão se você pode torná-lo simples?”

SendMessage usa reflexão e é extremamente lento em comparação com GetComponent e só deve ser considerado nos casos em que não afetará o jogo ou se você não tiver outra solução.

e que deve soar lógico, com GetComponent nós indicamos exatamente onde o script está localizado na memória. Com o SendMessage, o compilador conhece o objeto, mas ele vai passar por todos os scripts e funções até encontrar o vencedor sortudo. Isso pode afetar seu jogo se você usá-lo muitas vezes em vários objetos e se o objeto tem um monte de componentes e scripts (que são componentes, a propósito).

SendMessage procura apenas o objeto ao qual a chamada é feita, BroadcastMessage lança uma busca no objeto alvo, mas também em todos os seus filhos. A pesquisa será ainda mais longa se o objeto contiver uma grande hierarquia. Um recém-chegado apareceu com Unity4 SendMessageUpwards que você deveria ter adivinhado faz o mesmo que BroadcastMessage, mas para cima de modo que todos os pais do objeto até a raiz.

Ok, vamos considerar o nosso StormTrooper tem uma função para recebimento de ordem

//Order.csusing UnityEngine;using System.Collections;public class Order : MonoBehaviour{ private void ReceiveOrder(string order) { switch(order) { case "Fetch": //Action Debug.Log("Can you repeat Sir?"); break; // other cases } }}

Agora, vamos considerar o nosso StormTrooper atende a um comandante do Império:

// Commander.csprivate void Update(){ if(Input.GetKeyDown(KeyCode.Space)) { GameObject.Find("StormTrooper").SendMessage("ReceiveOrder","Fetch"); }}

Como você pode ver, o princípio permanece o mesmo, eu preciso de uma referência para o objeto de destino e, em seguida, enviar uma mensagem a ele. Assim como GetComponent é melhor se você tiver cache a referência do objeto.

também é possível adicionar um parâmetro para garantir que a mensagem encontra um receptor ou não.

Note que não é possível usar uma função retornando um valor. Você teria que voltar para cima e usar GetComponent.

funções interativas

unidade usa muitas características que fazem com que dois ou mais objetos interajam. A vantagem destas funções é que elas armazenam muitas informações sobre o outro objeto.

  1. Colisão funções OnTriggerXXXX/OnCollisionXXXX
  2. Física funções XXXXcast

Lá vamos nós com a colisão de declaração:

private void OnCollisionEnter(Collision colReference){}private void OnTriggerEnter(Collider colReference){}

A chamada da função cria uma variável do tipo de Colisão ou de Hadrões. A função preencherá esta instância com informações sobre a colisão e manterá uma referência ao outro objeto com o qual estamos interagindo.

prviate void OnCollisionEnter(Collision colReference){ GameObject goRef = colReference.gameObject; if(goRef.CompareTag("StormTrooper")) { Health script = goRef.GetComponent<Health>(); script.health -= 5; }}

Repare que eu não preciso olhar para o outro objeto como colReference é do tipo Colisão e contém uma referência a esse objeto, como membro. Agora posso executar o meu GetComponent como de costume e aceder à minha variável de saúde pública.

funções da classe física têm um princípio similar. Os dados não são armazenados em uma instância de colisão/Colisor, mas em uma instância de RaycastHit:

using UnityEngine;using System.Collections;public class CheckFlat : MonoBehaviour { private void Update() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { float result = Vector3.Dot(hit.normal,Vector3.up); if(Mathf.Abs(result) > 0.9) { Debug.Log("Ground is flat enough"); } else { Debug.Log("Not Flat Enough"); } } }}

I declare a variable of type RaycastHit, I pass it to the function. RaycastHit é uma estrutura e, em seguida, um tipo de valor, eu preciso da palavra-chave para criar uma referência a essa estrutura para que a função possa modificar a estrutura dentro. Se você está se perguntando sobre esse processo, confira o artigo de gerenciamento de memória e a seção sobre valor/tipo de referência.
O que eu estou fazendo aqui é verificar como plana é a superfície que eu estou colocando o cursor em cima de. Isto é útil para jogos estratégicos onde você quer colocar um edifício, mas não em um penhasco.
eu uso o produto Ponto do terreno normal com o Vector3.Para cima e se o resultado estiver perto o suficiente para 1 significa que eles estão alinhados, o chão é plano o suficiente para a construção.obviamente, mais alguns cálculos são necessários, pois este é apenas para um ponto e você precisa verificar o normal ao redor, mas você começa a idéia. Criei interacção entre a minha posição no rato e o terreno usando um mastro.

um raycast contém um ponto de partida e uma direcção. Em nosso caso, o ponto de partida é a conversão da posição do mouse para a posição do mundo e, em seguida, uma direção baseada na rotação da câmera e matriz de projeção. Esta linha vai infinitamente na direção definida (você também pode definir uma distância máxima) e se ele acerta um collider, a informação de que collider e colisão são armazenados no RaycastHit estrutura. A estrutura contém então uma referência ao objeto atingido.