Objetivo do sistema de interação
Um sistema de interação em primeira/terceira pessoa normalmente resolve três problemas: detectar o que o jogador está mirando, comunicar ao jogador que algo é interagível (feedback visual) e executar uma ação ao pressionar uma tecla (pegar, usar, inspecionar). Neste capítulo, você vai montar um fluxo completo com: Line Trace (raycast), filtragem por canal/objetos, highlight simples, tecla de Interagir, inventário básico via Array, uso/consumo de item e depuração com Draw Debug.
Arquitetura sugerida (simples e escalável)
- BP_PlayerCharacter: faz o Line Trace, decide o alvo atual, mostra feedback e chama a interação ao apertar a tecla.
- BP_InteractableBase: ator base para objetos interagíveis (pegar/usar/inspecionar). Contém dados do item e implementa a resposta ao interagir.
- Inventário simples: um
Arrayno Player com itens (por exemplo, uma struct com ID, nome, tipo, efeito). - Highlight: via Custom Depth/Stencil (recomendado) ou troca de material (mais simples, porém mais intrusivo).
Preparação: canal de trace e tags de interagível
Criar um Trace Channel para interação
Para evitar que o raycast acerte coisas irrelevantes (parede, chão, etc.), crie um canal dedicado.
- Project Settings > Collision > Trace Channels > New Trace Channel:
InteractTrace(Default Response: Ignore). - Nos objetos interagíveis, em Collision, defina a resposta para
InteractTracecomo Block.
Alternativa: Object Types (filtragem por tipo)
Se preferir, você pode usar LineTraceForObjects e filtrar por tipos (WorldDynamic, PhysicsBody). A abordagem por canal costuma ser mais previsível para interação.
Dados do item: struct e enum (inventário simples)
Para o inventário, crie uma estrutura de dados mínima para representar itens.
Enum de tipo de item
EItemType:Consumable,KeyItem,Inspectable.
Struct do item
Crie ST_ItemData com campos:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
ItemID(Name)DisplayName(Text)ItemType(EItemType)UseValue(Float) — por exemplo, cura/energiaPickupSFX(SoundBase, opcional)PickupVFX(NiagaraSystem/ParticleSystem, opcional)
No Player, crie Inventory como Array<ST_ItemData>.
BP_InteractableBase: ator interagível com ações
Componentes recomendados
- StaticMesh (ou SkeletalMesh): o visual do item.
- Sphere Collision (opcional): útil para limitar interação por proximidade, mas o Line Trace já resolve. Se usar, combine as duas coisas (trace + distância).
Variáveis
ItemData(ST_ItemData) — Instance Editable para configurar por item no level.bCanBePickedUp(bool)bCanBeInspected(bool)bCanBeUsedDirectly(bool) — se o item pode ser usado no mundo (ex.: botão/terminal) ou se só faz sentido no inventário.
Eventos/Functions no Interactable
Crie funções (ou eventos) para padronizar o comportamento:
GetInteractText(out Text): retorna “Pegar”, “Usar”, “Inspecionar” conforme o item.OnFocused(): liga highlight.OnUnfocused(): desliga highlight.Interact(Interactor): executa a ação principal quando o jogador pressiona Interagir.
Highlight simples com Custom Depth
Uma forma comum é habilitar Custom Depth no mesh e usar um Post Process para contorno. Mesmo sem Post Process, você pode usar Custom Depth como base para um highlight simples (dependendo do seu setup). O essencial aqui é alternar a flag.
- No
StaticMesh: marqueRender CustomDepth Passcomo false por padrão. - Em
OnFocused():Set Render CustomDepth= true. - Em
OnUnfocused():Set Render CustomDepth= false.
Se você não estiver usando Post Process, uma alternativa é trocar material para um “material emissivo” enquanto focado. Só evite esquecer de restaurar o material original (guarde referência).
Detecção: Line Trace no Player (raycast)
Variáveis no Player
InteractDistance(float) — ex.: 250–400.CurrentFocusedActor(Actor ref) — alvo atual.LastFocusedActor(Actor ref) — para desligar highlight ao trocar alvo.bDrawDebugInteract(bool) — para depuração.
Passo a passo: montar o trace
No Event Tick (ou em um Timer a cada 0.05s para otimizar), faça:
- Obter posição e direção da câmera:
Get Player Camera Managerou componente de câmera do personagem. Start= Camera Location.End= Start + (Camera Forward Vector * InteractDistance).- Executar
LineTraceByChannelusando o canalInteractTrace. - Ativar
Draw Debug TypecomoFor DurationquandobDrawDebugInteractfor true.
Start = CameraLocation End = Start + ForwardVector * InteractDistance LineTraceByChannel(Channel=InteractTrace, DrawDebug=Conditional)Filtragem e validação do alvo
Após o trace:
- Se
Hitfor false: não há alvo. Limpe foco e UI de interação. - Se
Hit Actorfor válido: confirme se é interagível. Você pode usar uma das estratégias:
- Por classe base:
CastparaBP_InteractableBase. - Por interface (mais flexível): criar uma Blueprint Interface
BPI_InteractablecomOnFocused,OnUnfocused,Interact,GetInteractText. (Se você já usa interfaces no projeto, essa é a melhor opção.) - Por tag:
Actor Has Tag= “Interactable”. Útil como filtro extra, mas não substitui a necessidade de funções de interação.
Mesmo usando classe base, trate o caso de “acertou algo que bloqueia o canal mas não é interagível” (erro comum quando configurações de colisão estão inconsistentes).
Gerenciar foco (ligar/desligar highlight)
Fluxo recomendado:
- Se o novo alvo é diferente do anterior: chame
OnUnfocusedno anterior (se válido) eOnFocusedno novo. - Atualize
LastFocusedActoreCurrentFocusedActor.
If HitActor != CurrentFocusedActor: If CurrentFocusedActor valid: CurrentFocusedActor.OnUnfocused() CurrentFocusedActor = HitActor CurrentFocusedActor.OnFocused()Se não houver Hit, mas CurrentFocusedActor era válido, chame OnUnfocused e zere a referência.
Tecla de Interagir: executar ação com tratamento de erros
Input Action
Crie uma Action Mapping (ou Enhanced Input) chamada Interact e associe a tecla E (ou a que preferir).
Lógica ao pressionar Interact
No evento de input:
- Se
CurrentFocusedActornão é válido: exiba um feedback leve (opcional) e saia. - Se é válido, chame
Interactno alvo, passando referência do Player (Interactor). - Se a chamada falhar (ex.: alvo destruído no mesmo frame), trate com
IsValidantes de chamar.
OnInteractPressed: If !IsValid(CurrentFocusedActor): PrintString("Sem alvo") return CurrentFocusedActor.Interact(self)Implementar “Pegar item”: ocultar/desativar, VFX/SFX e adicionar ao inventário
Regras do pickup
- O item deve sumir do mundo (ocultar mesh e desabilitar colisão) ou ser destruído.
- Deve tocar som/partícula (se configurado).
- Deve adicionar
ItemDataaoInventorydo Player.
Passo a passo no BP_InteractableBase (Interact)
Dentro de Interact(Interactor):
- Validar
Interactor(IsValid). Se inválido,PrintStringe retorne. - Checar
bCanBePickedUp. Se false, pode cair no modo “Inspecionar” ou “Usar” dependendo do item. - Adicionar ao inventário: no Interactor (Player), chame uma função
AddItemToInventory(ItemData). - Tocar SFX:
Play Sound at LocationcomPickupSFXse válido. - Tocar VFX:
Spawn System at LocationcomPickupVFXse válido. - Remover do mundo: opção A)
Set Actor Enable Collision(false)+Set Actor Hidden In Game(true); opção B)Destroy Actor.
Para evitar bugs de “pegar duas vezes”, desative colisão imediatamente antes de tocar efeitos ou adicionar ao inventário.
Função no Player: AddItemToInventory
No Player, crie:
AddItemToInventory(ItemData):
- Validar se
ItemData.ItemIDnão está vazio (opcional). Inventory.Add(ItemData).- Opcional:
PrintStringcom o nome do item para confirmar.
Implementar “Usar item”: consumir do inventário e aplicar efeito
Modelo simples de uso (consumível)
Vamos supor um item consumível que cura o jogador. Você pode “usar” via UI/inventário, mas aqui vamos focar na lógica: remover do Array e aplicar um efeito.
Variáveis de status no Player
Health(float)MaxHealth(float)
Função: UseItemByIndex (inventário)
No Player:
- Entrada:
Index(int). - Validar:
Inventory.IsValidIndex(Index). Se não,PrintString“Índice inválido” e retorne. Item = Inventory[Index].- Switch em
Item.ItemType:
- Consumable: aplicar efeito (ex.:
Health = Clamp(Health + Item.UseValue, 0, MaxHealth)) e entãoInventory.RemoveAt(Index). - KeyItem: normalmente não consome; pode apenas disparar lógica contextual (depende do jogo).
- Inspectable: abrir modo de inspeção (ver seção abaixo) sem consumir.
UseItemByIndex(Index): if !Inventory.IsValidIndex(Index) return Item = Inventory[Index] switch(ItemType) Consumable: Health = Clamp(Health + UseValue, 0, MaxHealth) Inventory.RemoveAt(Index)Usar item diretamente no mundo (opcional)
Se você quer que alguns atores sejam “usáveis” (ex.: alavanca), implemente em BP_InteractableBase um modo bCanBeUsedDirectly e, no Interact, execute uma ação sem adicionar ao inventário (por exemplo, tocar animação, mudar estado, abrir porta). Mantenha o mesmo tratamento de erros: alvo inválido, estado não permite uso, etc.
Implementar “Inspecionar”: modo simples e seguro
Inspecionar pode ser tão simples quanto mostrar um texto/tooltip, ou tão completo quanto “pegar o objeto e rotacionar na tela”. Aqui vai um modelo simples e prático sem UI complexa:
Inspeção por tooltip (rápida)
- No
GetInteractText, retorne “Inspecionar” para itens do tipoInspectable. - No
Interact, sebCanBeInspectedfor true, façaPrintStringcomItemData.DisplayNamee talvez uma descrição (se você adicionar no struct).
Inspeção com “freeze” e rotação (intermediária)
Se quiser um passo além:
- Ao inspecionar: desabilite input de movimento, mantenha o mouse para rotacionar um mesh em frente à câmera (pode ser um componente separado ou um ator “preview”).
- Saída da inspeção: reabilite input e destrua/oculte o preview.
Esse modo exige mais controle de câmera/UI, então mantenha como extensão opcional do sistema.
Feedback visual e texto de interação
Mostrar “Pressione E para ...”
Mesmo sem construir UI completa, você pode usar um Widget simples com um TextBlock. O Player atualiza o texto com base no alvo focado:
- Se
CurrentFocusedActorválido: chamarGetInteractTexte exibir. - Se inválido: esconder o widget.
Se você ainda não tem widget, use PrintString temporariamente para validar o fluxo.
Tratamento de erros comuns (e como evitar)
1) “Sem alvo” ao apertar Interagir
- Confirme se o trace está partindo da câmera (não do capsule).
- Aumente
InteractDistance. - Ative
bDrawDebugInteractpara ver a linha.
2) Trace acerta o objeto, mas não interage
- Verifique Collision do objeto: resposta ao canal
InteractTracedeve ser Block. - Verifique se o ator realmente é do tipo interagível (classe base/interface).
- Se estiver usando highlight por mesh, confirme que o mesh existe e é o componente correto.
3) Highlight “fica preso” quando olha para outro lugar
- Garanta que, quando não houver Hit, você chama
OnUnfocusedno alvo anterior. - Garanta que, ao trocar de alvo, você desliga o highlight do anterior antes de ligar no novo.
4) Pegar duas vezes o mesmo item
- Desabilite colisão imediatamente no início do pickup.
- Use um bool
bIsCollectedno interagível para bloquear reentrada.
Depuração com Draw Debug e logs
Draw Debug no Line Trace
Em LineTraceByChannel, use Draw Debug Type:
Noneem produção.For One Framepara checagens rápidas.For Durationpara observar enquanto testa.
Combine com PrintString exibindo: nome do Hit Actor, distância e o texto de interação retornado.
Tabela rápida: o que checar quando algo falha
| Sintoma | Checagem | Correção típica |
|---|---|---|
| Não aparece debug line | Tick/Timer está rodando? | Conectar execução e habilitar flag |
| Debug line passa pelo item | Canal/colisão | Block em InteractTrace |
| Hit ocorre, mas não há foco | Filtro de interagível | Classe base/interface/tag |
| Interagir não faz nada | Input mapping | Verificar Action/Enhanced Input e possess |
| Inventário não recebe item | Função AddItem | Validar Interactor e ItemData |
Checklist final do sistema (para testar rapidamente)
- Line Trace atinge apenas interagíveis (canal
InteractTrace). - Ao mirar: highlight liga; ao sair: highlight desliga.
- Tecla Interagir sem alvo: não quebra (tratamento com
IsValid). - Pegar item: toca SFX/VFX (se houver), some do mundo e entra no
Inventory. - Usar item consumível: aplica efeito e remove do Array.
- Draw Debug habilitável por bool para depuração.