Construct do Zero: Inimigos Simples com Padrões de Movimento e Detecção

Capítulo 8

Tempo estimado de leitura: 12 minutos

+ Exercício

Objetivo do capítulo

Neste capítulo você vai criar inimigos “inteligentes” sem programar, combinando behaviors e eventos para formar padrões de movimento e um sistema simples de estados: Patrulhando, Alertado e Atacando. Você também vai usar waypoints (marcadores), timers e variáveis para controlar cadência de ataque, dano e invulnerabilidade temporária.

Conceitos essenciais (sem repetir fundamentos)

Padrões de movimento com behaviors + eventos

  • Patrulha (vai e volta): o inimigo alterna entre dois pontos (waypoints) e inverte direção ao chegar.
  • Perseguição: o inimigo passa a seguir o jogador quando uma condição é atendida (distância e/ou linha de visão).
  • Estados: em vez de tentar controlar tudo com “se… então…” espalhado, você centraliza a lógica em um estado atual do inimigo. Isso organiza eventos e evita conflitos (por exemplo, patrulha e perseguição ao mesmo tempo).

Waypoints (marcadores) como alvos

Waypoints são sprites invisíveis (ou com opacidade baixa no editor) usados como “pontos de referência”. O inimigo não precisa saber coordenadas fixas: ele só precisa saber qual waypoint é o alvo atual.

Timers e variáveis para cadência

Para ataques e efeitos temporários, evite “esperar” com lógica confusa. Use:

  • Timer (por instância): para controlar intervalos de ataque, tempo de alerta, duração de invulnerabilidade.
  • Variáveis de instância: para guardar estado, alvo atual, vida, se está invulnerável, etc.

Preparação dos objetos do capítulo

Objetos necessários

  • Player (já existente no projeto).
  • Enemy_A: inimigo patrulheiro que vira perseguidor ao ver o jogador.
  • Enemy_B: inimigo sentinela que “acorda” por distância e ataca em cadência (ex.: investida ou tiro).
  • Waypoint: marcador genérico (pode ser um sprite simples).
  • Hitbox_Player (opcional): caso você use uma área separada para dano.
  • Projectile_B (opcional): se o Enemy_B atacar à distância.

Behaviors recomendados

  • Enemy_A: Pathfinding (para perseguir contornando obstáculos) ou MoveTo (mais simples, sem contorno). Para patrulha, você pode usar o mesmo behavior (MoveTo/Pathfinding) apontando para waypoints.
  • Enemy_B: MoveTo (para investida) ou nenhum (se for estacionário e só atirar).
  • Waypoint: sem behaviors.
  • Projectile_B (se usar): Bullet.

Variáveis de instância sugeridas

Crie estas variáveis em Enemy_A e Enemy_B (ajuste conforme seu jogo):

VariávelTipoExemploUso
stateTexto"patrol"Estado atual (patrol/alert/attack)
hpNúmero3Vida do inimigo
invulnBooleanofalseInvulnerabilidade temporária
targetUIDNúmero-1UID do waypoint alvo (patrulha)
speedPatrolNúmero80Velocidade na patrulha
speedChaseNúmero140Velocidade na perseguição
attackCooldownNúmero0.8Intervalo entre ataques

No Player, garanta que existam (ou crie) variáveis como hp e invuln para o exercício de dano.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

Waypoints na prática (patrulha vai e volta)

1) Criar waypoints e vincular ao inimigo

Crie dois waypoints para cada inimigo patrulheiro: WP_A1 e WP_A2 (pode ser o mesmo objeto Waypoint com uma variável tag).

Opção organizada: use um único objeto Waypoint com variável de instância tag (Texto). Exemplos: "A1", "A2", "B1", "B2".

2) Definir o primeiro alvo ao iniciar

Crie um grupo de eventos chamado ENEMY_A | Setup:

On created (Enemy_A)  -> Set state = "patrol"  -> Set hp = 3  -> Set invuln = false  -> Set speedPatrol = 80  -> Set speedChase = 140  -> Set attackCooldown = 0.8  -> Set targetUID = UID do waypoint inicial (ex.: o mais próximo com tag "A1")

Como pegar o waypoint inicial sem “hardcode”:

  • Selecione Waypoint com tag = "A1" e use ação “Pick nearest” em relação ao Enemy_A (ou posicione apenas um A1 por inimigo).
  • Depois, faça Enemy_A.targetUID = Waypoint.UID.

3) Movimento até o waypoint atual

Grupo ENEMY_A | Patrol (ativado quando state = "patrol"):

Enemy_A: state = "patrol"  -> MoveTo: Set speed = speedPatrol  -> MoveTo: Move to (Waypoint picked by UID = targetUID)

Para “pegar por UID”, use um evento que selecione o waypoint correto:

Enemy_A: state = "patrol"  -> Waypoint: Pick by UID (Enemy_A.targetUID)  -> Enemy_A: MoveTo move to Waypoint.X, Waypoint.Y

4) Trocar de alvo ao chegar (vai e volta)

Quando o inimigo estiver perto do waypoint, troque o alvo para o outro ponto:

Enemy_A: state = "patrol" AND Distance(Enemy_A, Waypoint(targetUID)) < 8  -> If Waypoint.tag = "A1" then set targetUID = UID do waypoint "A2"  -> Else set targetUID = UID do waypoint "A1"

Dica: em vez de comparar tags, você pode guardar targetTag no inimigo e alternar entre "A1" e "A2".

Perseguição por distância e linha de visão

Detecção por distância (rápida e eficiente)

Crie no Enemy_A variáveis:

  • detectRange (ex.: 220)
  • loseRange (ex.: 280) para evitar “piscar” entre estados

Grupo ENEMY_A | Detect:

Enemy_A: state = "patrol" AND Distance(Enemy_A, Player) < detectRange  -> Set state = "alert"  -> Start timer "alert" for 0.4s (Enemy_A)

O estado alert serve como transição (um “meio termo”) para dar tempo de animação/feedback e evitar mudanças bruscas.

Linha de visão (com Raycast)

Para linha de visão, você precisa de um objeto sólido/obstáculo (ex.: paredes) e usar um teste de “raio” entre inimigo e jogador. Em Construct, isso costuma ser feito com uma checagem de colisão ao longo de uma linha (Raycast) ou com um objeto auxiliar. A ideia é: se houver parede entre eles, não vê.

Estratégia simples e prática:

  • Use uma condição de “Line of sight” se disponível (alguns plugins/behaviors) ou simule com Raycast em eventos.
  • Se não tiver Raycast, use um “sensor” (um sprite fino) que aponta para o jogador e verifica colisão com paredes antes de confirmar.

Exemplo conceitual de evento (Raycast):

Enemy_A: state = "patrol" AND Distance(Enemy_A, Player) < detectRange AND Raycast(Enemy_A -> Player) hits Player before Wall  -> Set state = "alert"

Do alert para chase/attack

Grupo ENEMY_A | Alert:

Enemy_A: state = "alert" AND On timer "alert"  -> Set state = "attack"

Você pode renomear attack para chase se preferir separar “perseguir” de “atacar”. Aqui vamos usar attack como estado agressivo (persegue e tenta causar dano).

Estados do inimigo (organização por grupos)

Estrutura recomendada de grupos

  • ENEMY_A | Setup
  • ENEMY_A | Detect
  • ENEMY_A | Patrol
  • ENEMY_A | Attack
  • ENEMY_A | Damage & Invuln
  • ENEMY_B | Setup
  • ENEMY_B | Detect
  • ENEMY_B | Attack Pattern
  • GLOBAL | Player Damage & Invuln

Use a condição Enemy.state = "..." no topo de cada grupo para manter tudo previsível.

Enemy_A (Patrulha + perseguição + contato com dano)

1) Estado Attack: perseguir o jogador

Grupo ENEMY_A | Attack:

Enemy_A: state = "attack"  -> MoveTo: Set speed = speedChase  -> MoveTo: Move to Player.X, Player.Y

Para evitar perseguição infinita, volte para patrulha quando o jogador escapar:

Enemy_A: state = "attack" AND Distance(Enemy_A, Player) > loseRange  -> Set state = "patrol"

2) Dano por contato com cadência

Sem cadência, o jogador pode perder toda a vida em um único segundo. Use cooldown no inimigo ou invulnerabilidade no jogador (ou ambos).

Opção A (recomendada): invulnerabilidade no jogador após levar dano.

Enemy_A collides with Player AND Player.invuln = false  -> Subtract 1 from Player.hp  -> Set Player.invuln = true  -> Start timer "playerInvuln" for 0.8s (Player)

E o fim da invulnerabilidade:

Player: On timer "playerInvuln"  -> Set Player.invuln = false

Feedback visual rápido (piscar):

Player.invuln = true  -> Set Player opacity to 50 (ou alternar com a função Sine/Timer) Player.invuln = false  -> Set Player opacity to 100

Enemy_B (Sentinela com padrão diferente: alerta por distância + ataque em cadência)

Ideia do padrão

O Enemy_B não patrulha. Ele fica “guardando” e, quando o jogador entra no raio, ele entra em alert e começa a atacar com cadência. Você pode escolher:

  • Ataque à distância (projétil).
  • Investida (dash curto em direção ao jogador).

A seguir, um passo a passo para o modelo de projétil (mais fácil de controlar e visualizar).

1) Setup do Enemy_B

On created (Enemy_B)  -> Set state = "patrol" (ou "idle")  -> Set hp = 2  -> Set invuln = false  -> Set detectRange = 260  -> Set loseRange = 320  -> Set attackCooldown = 1.2

Se preferir, use state = "idle" em vez de patrol para o Enemy_B.

2) Detectar e entrar em ataque

Enemy_B: state = "idle" AND Distance(Enemy_B, Player) < detectRange  -> Set state = "attack"  -> Start timer "shoot" for 0.1s (Enemy_B)

O timer inicial curto serve para o primeiro ataque acontecer logo após detectar.

3) Atirar com timer (cadência)

Grupo ENEMY_B | Attack Pattern:

Enemy_B: state = "attack" AND On timer "shoot"  -> Spawn Projectile_B at Enemy_B (image point "muzzle" ou centro)  -> Projectile_B: Set angle toward Player  -> Projectile_B: Set speed (ex.: 420)  -> Enemy_B: Start timer "shoot" for attackCooldown

Se o projétil usar Bullet behavior, você pode definir o ângulo e a velocidade diretamente no behavior.

4) Parar de atacar quando o jogador sair

Enemy_B: state = "attack" AND Distance(Enemy_B, Player) > loseRange  -> Set state = "idle"

5) Dano do projétil + invulnerabilidade do jogador

Projectile_B collides with Player AND Player.invuln = false  -> Destroy Projectile_B  -> Subtract 1 from Player.hp  -> Set Player.invuln = true  -> Start timer "playerInvuln" for 0.8s (Player)

Também destrua o projétil ao bater em paredes/solids:

Projectile_B collides with Wall  -> Destroy Projectile_B

Dano no inimigo e invulnerabilidade temporária (quando o jogador ataca)

Fonte de dano do jogador

Você pode ter um objeto de ataque (ex.: SwordHitbox) que só aparece durante a animação de ataque, ou usar colisão com um projétil do jogador. O padrão é o mesmo: ao colidir, reduzir HP e ativar invulnerabilidade do inimigo por um curto período.

Eventos de dano (para Enemy_A e Enemy_B)

Grupo ENEMIES | Damage & Invuln (pode separar por inimigo se preferir):

SwordHitbox overlaps Enemy_A AND Enemy_A.invuln = false  -> Subtract 1 from Enemy_A.hp  -> Set Enemy_A.invuln = true  -> Start timer "invuln" for 0.25s (Enemy_A)  -> Start timer "hitFlash" for 0.25s (Enemy_A)
Enemy_A: On timer "invuln"  -> Set invuln = false

Feedback visual no inimigo (piscar/vermelho):

Enemy_A.invuln = true  -> Set Enemy_A blend mode/additive OR set color tint OR set opacity 60 Enemy_A.invuln = false  -> Restore visual

Morte do inimigo:

Enemy_A.hp <= 0  -> Spawn effect (opcional)  -> Destroy Enemy_A

Repita a mesma lógica para Enemy_B.

Exercício guiado: dois inimigos diferentes com eventos organizados

Regras do exercício

  • Você deve implementar Enemy_A (patrulha vai-e-volta + persegue por distância/linha de visão) e Enemy_B (sentinela que atira com cadência).
  • Os eventos devem estar organizados em grupos por inimigo e por responsabilidade (setup, detecção, estados, dano).
  • Deve existir lógica de dano no jogador e nos inimigos.
  • Deve existir invulnerabilidade temporária após receber dano (no jogador e nos inimigos).

Checklist de implementação (passo a passo)

  1. Crie os waypoints do Enemy_A (A1 e A2) e posicione no layout.

  2. Adicione variáveis em Enemy_A e Enemy_B: state, hp, invuln, attackCooldown, detectRange, loseRange. No Enemy_A adicione targetUID e velocidades.

  3. Enemy_A | Setup: ao criar, definir estado patrulha e escolher waypoint inicial.

  4. Enemy_A | Patrol: mover até o waypoint atual e alternar ao chegar (distância < 8).

  5. Enemy_A | Detect: se jogador entrar no raio e (se aplicável) tiver linha de visão, mudar para alert e depois attack via timer.

  6. Enemy_A | Attack: perseguir o jogador e voltar para patrulha se distância > loseRange.

  7. Enemy_B | Setup: estado idle, ranges e cooldown.

  8. Enemy_B | Detect: entrar em attack ao detectar o jogador e iniciar timer shoot.

  9. Enemy_B | Attack Pattern: no timer shoot, criar projétil mirando no jogador e reiniciar timer com attackCooldown.

  10. GLOBAL | Player Damage & Invuln: ao colidir com Enemy_A ou Projectile_B, reduzir HP e ativar Player.invuln por timer.

  11. ENEMIES | Damage & Invuln: ao receber golpe do jogador, reduzir HP, ativar invulnerabilidade curta e destruir ao chegar em 0.

Organização sugerida dos eventos (modelo)

GrupoConteúdo
ENEMY_A | SetupVariáveis iniciais, escolher waypoint inicial
ENEMY_A | DetectDistância + linha de visão, transição para alert
ENEMY_A | PatrolMoveTo para waypoint, alternância A1/A2
ENEMY_A | AlertTimer curto para entrar em attack
ENEMY_A | AttackPerseguir, perder alvo e voltar
ENEMY_B | SetupVariáveis iniciais
ENEMY_B | DetectEntrar em attack e iniciar timer de tiro
ENEMY_B | Attack PatternTimer shoot, spawn projétil, cooldown
GLOBAL | Player Damage & InvulnContato/projétil, invulnerabilidade, feedback
ENEMIES | Damage & InvulnReceber golpe, invuln curta, morte

Ajustes finos (para ficar “com cara de jogo”)

Evitar tremedeira ao chegar no waypoint

  • Aumente o raio de chegada (ex.: 10–16).
  • Ao trocar de waypoint, espere 0.05s antes de mandar mover novamente (timer curto) se notar oscilação.

Evitar alternância rápida entre patrulha e ataque

  • Use detectRange menor que loseRange (histerese).
  • Use o estado alert com timer curto antes de atacar.

Cadência consistente

  • Centralize ataques em timers (por instância) e reinicie o timer ao atacar.
  • Não use “Every X seconds” global se você quer controle por inimigo (isso pode sincronizar todos).

Agora responda o exercício sobre o conteúdo:

Qual abordagem evita que o jogador perca muita vida rapidamente ao encostar em um inimigo, mantendo uma cadência de dano controlada?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Ao ativar invulnerabilidade temporária (ex.: Player.invuln) e encerrá-la com um timer, o contato não causa dano contínuo a cada frame, criando uma cadência consistente.

Próximo capitúlo

Construct do Zero: Sistema de Vida, Dano, Coleta e Pontuação

Arrow Right Icon
Capa do Ebook gratuito Construct do Zero: Desenvolvendo Jogos 2D Sem Programar (com Eventos)
44%

Construct do Zero: Desenvolvendo Jogos 2D Sem Programar (com Eventos)

Novo curso

18 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.