Voltar para o Blog
Quest Log

GetComponent na Unity: Como Acessar Outros Componentes

Personagem de jogo com peças encaixáveis ao redor, cada peça representando um componente ligado por fios ao corpo

Unity GetComponent sem pesar o jogo: cache no Awake, TryGetComponent na colisão, GetComponentInChildren e InParent numa hitbox, com código C# do Unity 6.3.

Ferramenta:UnityC#

GetComponent na Unity: Como Acessar Outros Componentes

Unity GetComponent é o método que um script usa para pegar outro componente do mesmo GameObject: o Rigidbody2D para mover, o SpriteRenderer para piscar, o Animator para trocar animação. A forma básica é GetComponent<Rigidbody2D>(), e o jeito certo de usar é chamar uma vez no Awake, guardar numa variável e nunca mais procurar.

Aqui você vai ver onde chamar cada variação sem pesar o jogo: cache no Awake, TryGetComponent na colisão, GetComponentInChildren e GetComponentInParent com uma hitbox de verdade, e quando é melhor arrastar a referência no Inspector. Todo o código é para a Unity 6.3 LTS.

O que GetComponent faz

Na Unity, um GameObject é uma caixa vazia com um Transform. O comportamento vem dos componentes que você adiciona: SpriteRenderer, Collider2D, Rigidbody2D, Animator e os seus scripts. Cada componente é um objeto separado, e um script não enxerga os vizinhos automaticamente.

GetComponent<T>() percorre os componentes do GameObject em que o script está e devolve o primeiro do tipo T. Se não achar, devolve null. O T pode ser um tipo da Unity, um script seu ou uma interface que algum componente implemente.

Isso explica dois comportamentos que pegam iniciante. Primeiro, ele só procura no mesmo GameObject: um SpriteRenderer que está num filho não é encontrado. Segundo, se o objeto tem dois componentes do mesmo tipo (dois BoxCollider2D, por exemplo), você recebe um deles, e a ordem não é algo em que você deve confiar.

Unity getcomponent: sintaxe e cache no Awake

O padrão que vai em quase todo script. Arquivo PlayerMovement.cs:

using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Rigidbody2D))]
public class PlayerMovement : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 6f;

    private Rigidbody2D body;
    private SpriteRenderer spriteRenderer;
    private InputAction moveAction;
    private float horizontalInput;

    private void Awake()
    {
        body = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        moveAction = InputSystem.actions.FindAction("Move");
    }

    private void Update()
    {
        horizontalInput = moveAction.ReadValue<Vector2>().x;

        if (horizontalInput != 0f)
        {
            spriteRenderer.flipX = horizontalInput < 0f;
        }
    }

    private void FixedUpdate()
    {
        body.linearVelocity = new Vector2(horizontalInput * moveSpeed, body.linearVelocity.y);
    }
}

Três decisões neste script:

  1. Cache no Awake. O GetComponent roda uma vez por instância. O Update e o FixedUpdate usam as variáveis body e spriteRenderer, que são só uma leitura de campo. O Awake é o lugar certo porque ele serve para o objeto preparar a si mesmo, e o componente está no mesmo objeto.
  2. [RequireComponent(typeof(Rigidbody2D))]. Ao adicionar o script, a Unity adiciona o Rigidbody2D junto se ele não existir e impede que alguém remova o Rigidbody2D enquanto o script estiver lá. É uma garantia em tempo de editor de que o GetComponent nunca vai voltar null.
  3. Campo private. Ninguém de fora precisa trocar o Rigidbody do jogador. Se outro script precisar, exponha um método.

Por que não chamar no Update? Uma chamada é rápida, mas ela procura na lista de componentes toda vez. Com 200 inimigos fazendo três GetComponent por frame a 60 FPS, são 36 mil buscas por segundo para pegar sempre as mesmas referências. O cache troca isso por zero. Se você quer entender onde a performance realmente vai embora num jogo, o post otimização de performance para 60 FPS mostra como medir antes de otimizar.

TryGetComponent em colisão e trigger

Na colisão, você não sabe de antemão o que bateu em você. Pode ser o jogador, uma parede, uma moeda. O TryGetComponent faz a pergunta e entrega a resposta numa linha. Arquivo DamageZone.cs:

using UnityEngine;

public class DamageZone : MonoBehaviour
{
    [SerializeField] private int damage = 1;

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.TryGetComponent(out Health health))
        {
            health.TakeDamage(damage);
        }
    }
}

E o Health.cs que ele procura:

using UnityEngine;

public class Health : MonoBehaviour
{
    [SerializeField] private int maxHealth = 3;
    private int currentHealth;

    private void Awake()
    {
        currentHealth = maxHealth;
    }

    public void TakeDamage(int amount)
    {
        currentHealth -= amount;
        Debug.Log(name + " ficou com " + currentHealth + " de vida.");

        if (currentHealth <= 0)
        {
            Destroy(gameObject);
        }
    }
}

Compare com a versão sem Try: Health health = other.GetComponent<Health>(); if (health != null) { ... }. Faz a mesma coisa em duas linhas. A documentação de Component.TryGetComponent acrescenta um detalhe técnico: quando o componente não existe, ele não aloca memória no Editor, diferente do GetComponent. Isso só importa no Editor, mas é justamente onde você testa, e trigger acontece toda hora.

Repare que aqui não tem como fazer cache: cada colisão traz um objeto diferente. Esse é o uso legítimo de buscar componente durante o jogo, porque acontece num evento, não a cada frame.

Uma variação útil é procurar por interface. Se baús, inimigos e caixas quebráveis implementam uma interface IDamageable, o DamageZone faz TryGetComponent(out IDamageable target) e funciona com todos, sem saber qual é qual. Esse é o padrão Component aplicado, explicado em padrões de projeto em jogos.

A interface fica num arquivo próprio, IDamageable.cs:

public interface IDamageable
{
    void TakeDamage(int amount);
}

O Health passa a declarar public class Health : MonoBehaviour, IDamageable, e um script Breakable de caixa faz o mesmo com a própria regra (vira pedaços no primeiro golpe, por exemplo). Na zona de dano, a linha muda só no tipo: if (other.TryGetComponent(out IDamageable target)) { target.TakeDamage(damage); }. O ganho aparece quando o projeto cresce: um espinho, uma bala e uma explosão causam dano em qualquer coisa que saiba receber dano, e você não precisa editar o espinho cada vez que inventa um objeto novo. Um cuidado: a interface não é um componente da Unity, então ela não aparece no Inspector nem funciona com RequireComponent. Ela só existe para o código perguntar "você sabe tomar dano?".

Até aqui são peças pequenas. A dificuldade real começa quando inimigo, arma, animação e vida precisam funcionar juntos num jogo inteiro, com prefab reaproveitado, hierarquia de vários níveis e bug que só aparece na fase três. É nesse ponto que estudar sozinho costuma travar.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

GetComponentInChildren e GetComponentInParent

Personagem de jogo raramente é um GameObject só. O padrão comum é um objeto raiz com o script e o Rigidbody, um filho "Visual" com o SpriteRenderer e o Animator, e um filho "Hurtbox" com o collider que recebe golpe. Isso permite girar o visual sem girar a física e ter hitboxes de tamanhos diferentes.

Inimigo (Rigidbody2D, Health, EnemyAI)
├── Visual (SpriteRenderer, Animator)
└── Hurtbox (BoxCollider2D, Is Trigger)

Dois problemas aparecem com essa estrutura. O script na raiz precisa do Animator, que está num filho. E quando a espada do jogador acerta a Hurtbox, o other do trigger é o collider da Hurtbox, que não tem Health.

Para o primeiro, GetComponentInChildren. Arquivo EnemyAI.cs:

using UnityEngine;

public class EnemyAI : MonoBehaviour
{
    private Animator animator;
    private static readonly int IsWalking = Animator.StringToHash("IsWalking");

    private void Awake()
    {
        animator = GetComponentInChildren<Animator>();
    }

    public void SetWalking(bool walking)
    {
        animator.SetBool(IsWalking, walking);
    }
}

Para o segundo, GetComponentInParent na arma. Arquivo SwordHitbox.cs:

using UnityEngine;

public class SwordHitbox : MonoBehaviour
{
    [SerializeField] private int damage = 2;

    private void OnTriggerEnter2D(Collider2D other)
    {
        Health health = other.GetComponentInParent<Health>();
        if (health != null)
        {
            health.TakeDamage(damage);
        }
    }
}

O que a documentação garante sobre esses dois: GetComponentInChildren sempre procura primeiro no próprio GameObject e depois desce pelos filhos, em profundidade. O parâmetro includeInactive vale false por padrão, então filhos desativados são ignorados; passe true se o componente pode estar num objeto escondido. GetComponentInParent faz o caminho inverso: começa no próprio objeto e sobe pelos pais. Ou seja, o SwordHitbox também funciona se o Health estiver no mesmo objeto do collider.

Existem também as versões no plural (GetComponents, GetComponentsInChildren), que devolvem todos. Servem, por exemplo, para trocar a cor de todos os SpriteRenderer de um personagem montado em partes.

Referência pelo Inspector vs GetComponent

Existe um terceiro caminho, e muitas vezes é o melhor: arrastar a referência no Inspector.

using UnityEngine;

public class EnemyVisual : MonoBehaviour
{
    [SerializeField] private SpriteRenderer spriteRenderer;
    [SerializeField] private Animator animator;

    public void Flash(Color color)
    {
        spriteRenderer.color = color;
    }
}

Com [SerializeField] private, o campo aparece no Inspector e você arrasta o filho "Visual" para ele. Sem busca nenhuma em tempo de jogo.

Quando usar cada um:

  • GetComponent no Awake: componente no mesmo objeto, sempre presente. Combine com RequireComponent.
  • Inspector: componente em outro objeto (filho específico, objeto da cena, outro prefab), ou quando há mais de um do mesmo tipo e você precisa de um específico. Deixa a ligação visível para quem abre o prefab.
  • TryGetComponent / InParent em evento: o outro objeto só é conhecido na hora da colisão, do raycast ou do clique.
  • GetComponentInChildren no Awake: quando a hierarquia do prefab pode mudar e você quer que o script ache sozinho. É mais frágil que o Inspector se houver dois componentes do mesmo tipo nos filhos.

Uma prática que junta os dois: arrastar no Inspector e usar o método Reset() para preencher automaticamente. A Unity chama Reset quando você adiciona o componente ou clica em Reset no menu dele, então private void Reset() { spriteRenderer = GetComponentInChildren<SpriteRenderer>(); } preenche o campo no editor e você só confere.

Erros comuns: null, componente errado, chamar no Update

NullReferenceException na linha que usa o componente. O GetComponent não achou nada. Causas, em ordem de frequência: o componente está num filho (use InChildren ou o Inspector), o script está no objeto errado, ou o tipo é diferente (Rigidbody em vez de Rigidbody2D, Collider em vez de Collider2D). O post NullReferenceException na Unity tem o roteiro completo de investigação.

Física 3D e 2D misturadas. GetComponent<Rigidbody>() num objeto 2D volta null, sempre. Os tipos 2D terminam em "2D" e não se misturam com os 3D.

GetComponent no Update. Funciona, mas é busca repetida à toa. Cache no Awake.

GetComponent de outro objeto no Awake. Pegar componente do próprio objeto no Awake é seguro. Ler dados de outro objeto no Awake não é, porque ele pode não ter inicializado ainda. Pegue a referência e leia os valores no Start.

Esperar que GetComponent procure na cena. Ele procura num GameObject. Para achar algo na cena, existem FindFirstObjectByType<T>() e FindAnyObjectByType<T>(), que substituem o antigo FindObjectOfType. Use no Start, uma vez, e só quando arrastar no Inspector não for possível.

Próximo passo

Monte o inimigo com a hierarquia raiz, Visual e Hurtbox, e faça a espada do jogador causar dano via GetComponentInParent. Depois troque o GetComponentInChildren do EnemyAI por um campo arrastado no Inspector e compare qual dos dois você prefere manter num prefab que vai ser duplicado vinte vezes. Quando um null aparecer no caminho, o post sobre NullReferenceException é a parada seguinte.

Perguntas frequentes

GetComponent é pesado na Unity?

Uma chamada isolada é barata. O problema é chamar dentro do Update para pegar o mesmo componente todo frame, em muitos objetos. Guarde o resultado numa variável no Awake e use a variável no resto do script.

Qual a diferença entre GetComponent e TryGetComponent?

GetComponent devolve o componente ou null. TryGetComponent devolve true ou false e entrega o componente num parâmetro out, o que deixa o código de colisão mais limpo. A documentação também registra que TryGetComponent não aloca memória no Editor quando o componente não existe.

GetComponentInChildren procura no próprio objeto?

Sim. Ele sempre procura primeiro no GameObject em que foi chamado e depois desce pelos filhos em profundidade. Por padrão ignora filhos inativos; passe true no parâmetro includeInactive para incluir.

Como pegar o Rigidbody2D com GetComponent?

Declare um campo private Rigidbody2D body e, no Awake, faça body = GetComponent<Rigidbody2D>(). Acrescente [RequireComponent(typeof(Rigidbody2D))] na classe para a Unity garantir que o componente exista no objeto.