2D Character Movement Pack

Controlador de player completo: movimento, pulo, dash, combate corpo-a-corpo com combo, vida, reações de hit e feedback de impacto — pronto para usar no seu projeto.

Visão Geral

Este pacote entrega um personagem 2D jogável e completo: movimento de corrida/pulo com controle aéreo, pulo e dash com buffer de input, um sistema de combo corpo-a-corpo 2D com hitboxes e dano, um componente de vida com knockback e ganchos de defesa, animações de reação a hit, e efeitos leves de "game feel" (hit-stop, tremor de câmera, slow motion) — sem nenhuma dependência de terceiros.

Todos os scripts vivem sob o namespace CloudedStudio.CharacterPack2D e são compilados em sua própria assembly (CloudedStudio.CharacterPack2D.asmdef), então eles não vão deixar a compilação do seu projeto mais lenta e não vão colidir com os nomes das suas próprias classes.

Para quem é esta documentação Esta documentação assume familiaridade básica com o Editor da Unity (adicionar componentes, configurar campos no Inspector, Animation Events) e o pacote new Input System. Não é necessário escrever código para ter a configuração padrão funcionando — programação só é necessária se você quiser estender o sistema de combate (por exemplo, seu próprio inimigo ou componente de defesa).

Conteúdo do Pacote

Assets/Clouded Studio/2D Character Movement Pack/Scripts/ ├── Core/ Contratos e tipos de dados compartilhados │ ├── DamageInfo.cs Struct que descreve um único golpe │ ├── IDamageable.cs Interfaces IDamageable + IKnockbackable │ ├── IDefendable.cs Interface IDefendable + DefenseResult │ ├── IVariableVulnerability.cs Interface IVariableVulnerability │ └── IFacingDirectionProvider.cs Interface IFacingDirectionProvider ├── Movement/ │ ├── CharacterMovement.cs Movimento horizontal, aceleração, forças externas │ ├── CharacterGroundChecker.cs Detecção de chão + coyote time + friction dinâmico │ ├── CharacterJump.cs Pulo com buffer, altura variável, controle aéreo │ ├── PlayerJump.cs Subclasse de pulo específica do player │ ├── CharacterDashRoll.cs Dash/rolada com buffer e interrupção por virada │ └── CharacterInputController.cs Ponte do Input System → movimento/pulo/dash/ataque ├── Player/ │ ├── PlayerController.cs Rig do player + estado de combo + direção de frente │ └── PlayerHitCounter.cs Contador de hits consecutivos (para UI) ├── Combat/ │ ├── CharacterHitbox.cs Collider(s) de trigger de um único ataque │ ├── CharacterHitboxController.cs Ativa/desativa hitboxes, espelha ao virar │ ├── MeleeDamageDealer.cs Pipeline de hitbox → dano + knockback │ ├── HealthComponent.cs Vida, dano, knockback, eventos de morte │ ├── PlayerHitSequence.cs Toca a animação de reação "Player-Hit" ao receber dano │ └── FX/ │ ├── CameraShake2D.cs Tremor de câmera autocontido (sem pacote de câmera de terceiros) │ ├── HitImpactEffect.cs Slow motion + tremor quando o player leva dano │ ├── HitConnectEffect.cs Hit-stop + tremor quando o ataque do player acerta │ └── PlayerReceiveHitEffect.cs Spawna VFX quando o player leva dano └── Animations/ └── PlayerAnimationStatesController.cs Atualiza parâmetros do Animator, Animation Events de hitbox

Início Rápido

O caminho mais rápido para ter um player que se move, pula e ataca. Cada passo é detalhado depois em sua própria seção — siga os links se algo não funcionar.

  1. Crie um GameObject de player com um Rigidbody2D (Body Type: Dynamic, Gravity Scale ~3-5, Freeze Rotation Z) e um Collider2D sem trigger para o corpo.
  2. Adicione CharacterGroundChecker, CharacterMovement, PlayerJump e PlayerController — veja Movimento & Pulo.
  3. Adicione um componente PlayerInput (pacote Input System) e CharacterInputController — veja Configuração de Input.
  4. Adicione um Animator com os parâmetros exigidos e PlayerAnimationStatesController — veja Configuração do Animator.
  5. (Opcional) Adicione CharacterDashRoll para ter dash — veja Dash / Rolada.
  6. Construa as hitboxes de ataque como objetos filhos e adicione CharacterHitboxController + MeleeDamageDealer — veja Combate & Hitboxes.
  7. Adicione HealthComponent ao player (e a qualquer outra coisa que deva poder receber dano) — veja Vida & Dano.
  8. (Opcional) Adicione os componentes de FX para feedback de impacto — veja Feedback de Hit.

Controles Padrão

As cenas de demonstração incluídas (Demo/PlayerSampleScene.unity e Demo/PlayerPrototypeSampleScene.unity) já vêm com um asset PlayerInputs.inputactions pronto para uso. Estas são as teclas padrão configuradas:

AçãoTeclas
MoverSetas do teclado (Cima / Baixo / Esquerda / Direita)
PularZ ou Espaço
AtacarX
DashC

Esses binds estão definidos inteiramente dentro do PlayerInputs.inputactions — abra-o no editor de Input Actions caso queira adicionar, remover ou remapear alguma tecla.

Movimento & Pulo

Componentes obrigatórios

ComponenteFinalidade
Rigidbody2DExigido por tudo abaixo. Defina Body Type como Dynamic e congele a rotação em Z.
CharacterGroundCheckerDetecta contato com o chão via Physics2D, com buffer de coyote time.
CharacterMovementAceleração/desaceleração horizontal e a API de "força externa" usada por pulo/dash/knockback.
PlayerJumpPulo com buffer e altura variável (cortado ao soltar o botão).
PlayerControllerAgrega o rig acima e rastreia o estado de combo/ataque/direção. Outros scripts referenciam este único componente em vez de conectar cada peça individualmente.

Campos do CharacterGroundChecker

CampoO que faz
Ground LayerLayerMask do que conta como "chão" — defina para suas layers de Chão/Plataforma.
Dynamic FrictionDeixe ativado. Troca o PhysicsMaterial2D do Rigidbody2D entre um material "no chão" e um "no ar" para o personagem não ficar preso em paredes durante um pulo.
Airborne Physics Material 2DUm PhysicsMaterial2D com friction baixo/zero. Crie um via Assets > Create > 2D > Physics Material 2D.
Grounded Physics Material 2DSeu material de friction normal de gameplay (pode ser o mesmo que já está no Rigidbody2D).
Por que dois materiais? O Box2D armazena o friction em um contato no momento em que ele é criado. Alterar o valor de friction do material existente em tempo de execução não faz contatos já ativos reconhecerem a mudança — o que faz o player parecer "preso" a uma parede durante um pulo. Trocar a referência do material força o Box2D a recriar o contato e aplicar o novo friction imediatamente.

Campos do CharacterMovement

CampoPadrãoO que faz
Initial Speed2Velocidade para a qual o personagem é "encaixado" instantaneamente ao começar a se mover do zero.
Max Speed8Velocidade horizontal máxima.
Acceleration30Unidades/segundo² aplicadas enquanto o personagem se move em direção à velocidade alvo.
Deceleration Factor1.5Multiplicador sobre Acceleration usado ao parar (sem input pressionado).

Campos do PlayerJump / CharacterJump

CampoPadrãoO que faz
Buffer Jump Window0.15sPor quanto tempo um pulo pressionado um pouco antes da hora é lembrado, executando assim que as condições de chão/coyote permitirem.
Jump Force16.5Impulso de velocidade para cima aplicado no pulo.
Cutting Factor0.5Multiplica a velocidade vertical restante quando o botão de pulo é solto antes da hora — gera altura de pulo variável.
Air Control Factor0.8 (0-1)Quanta aceleração horizontal se aplica ao tentar reverter a direção no ar. 1 = controle total (como no chão), 0 = momentum não pode ser revertido no ar.

Conecte seu botão de pulo para chamar PlayerJump.Jump() ao pressionar e PlayerJump.CutJump() ao soltar — isso é feito automaticamente se você usar o CharacterInputController (veja a próxima seção).

Configuração de Input

O CharacterInputController conecta o Input System da Unity aos componentes de movimento/pulo/dash/ataque. Ele depende do comportamento de notificação Send Messages do componente PlayerInput, então não há conexão manual de eventos.

  1. Adicione um componente PlayerInput (do pacote Input System) ao GameObject do player.
  2. Defina seu Behavior como Send Messages.
  3. Atribua (ou crie) um Input Actions asset com um action map Player contendo estas ações:
    Nome da açãoTipoBinding sugerido
    MoveValue / Vector2 (composto 2D Vector, ou um stick de gamepad)WASD / Stick Esquerdo
    JumpButton — adicione uma Interaction "Press" definida como "Press and Release" (veja o aviso abaixo)Espaço / Botão Sul
    DashButtonShift Esquerdo / Botão Leste
    AttackButtonBotão Esquerdo do Mouse / Botão Oeste
  4. Adicione CharacterInputController ao mesmo GameObject. Seus campos de referência se preenchem automaticamente via GetComponent no Reset se deixados vazios. Defina Action Map Name para corresponder ao action map criado acima (padrão: "Player").
O Jump precisa de uma Interaction "Press and Release", ou o corte de pulo não funciona Uma action do tipo Button sem nenhuma Interaction atribuída usa por padrão uma Interaction implícita Press no modo Press Only. Esse modo só reporta o botão como pressionado — ele nunca dispara a fase Canceled da action no release, então o OnJump nunca recebe isPressed = false e CharacterJump.CutJump() nunca é chamado. O pulo sempre vai atingir a altura máxima, não importa quão rápido você solte o botão.

Correção: selecione a action Jump, adicione uma Interaction Press no painel de Interactions, e defina seu Trigger Behavior como Press and Release (não o padrão "Press Only"). Isso faz a action reportar tanto o press quanto o release, que é o que a altura de pulo variável precisa.
Renomeando ações Se seu projeto já tem um Input Actions asset com nomes de ação diferentes, você não precisa renomeá-los — apenas garanta que os nomes de método em C# que o "Send Messages" do PlayerInput procura correspondam: OnMove, OnJump, OnDash, OnAttack. Esses são exatamente os quatro métodos públicos que o CharacterInputController expõe, então, desde que seu action map use esses nomes (Input Actions → ação → campo "Name"), vai funcionar.
O Behavior precisa ser "Send Messages" Se o PlayerInput.Notification Behavior estiver definido como Invoke Unity Events ou Invoke C# Events, os métodos do CharacterInputController não serão chamados automaticamente. Ou troque para Send Messages, ou conecte os eventos manualmente no Inspector chamando OnMove/OnJump/OnDash/OnAttack.

Usando o Input Actions asset "Project-wide"

Se você atribuir o asset Project-wide Actions da Unity (Project Settings > Input System Package) ao PlayerInput, o Inspector vai exibir o aviso:

Project-wide actions asset is not recommended to be used with Player Input
because it is a singleton reference and all actions maps are enabled by
default. You should manually disable all action maps on Start() and
manually enable the default action map.

Isso acontece porque o asset Project-wide é uma única instância compartilhada, então por padrão todos os action maps que ele contém (Player, UI, qualquer outro que você tenha definido) ficam ativos ao mesmo tempo — um input destinado a um menu poderia "vazar" para o gameplay, e vice-versa. Esse é um comportamento conhecido do Input System, não é específico deste pacote.

O CharacterInputController já trata isso para você: ao ser habilitado, ele desativa todo action map em PlayerInput.actions e reativa apenas o que estiver no campo Action Map Name — exatamente a solução que o aviso da Unity recomenda. Você pode ignorar o aviso com segurança assim que este componente estiver presente. Se preferir não ver o aviso de forma alguma, crie um Input Actions asset dedicado para este personagem em vez de usar o Project-wide (o Project Settings continua permitindo que outros sistemas usem suas próprias actions project-wide de forma independente).

Dash / Rolada opcional

Adicione CharacterDashRoll ao player (exige CharacterMovement e CharacterJump no mesmo GameObject). É acionado automaticamente pela ação de input Dash se você estiver usando o CharacterInputController, ou chame CharacterDashRoll.TriggerDash() você mesmo.

CampoPadrãoO que faz
Dash Speed Multiplier2Velocidade do dash = CharacterMovement.MaxSpeed × este valor.
Dash Duration0.3sQuanto tempo o dash dura antes do controle voltar ao movimento normal.
Buffer Dash Window0.15sSe Dash for pressionado no ar, o input é lembrado por esse tempo — aterrissar dentro da janela executa o dash automaticamente.
Player refautoUsado para ler a direção de frente do dash. Preenchido automaticamente via GetComponent.

Pressionar a direção oposta no meio do dash o interrompe imediatamente e dispara os triggers de Animator DashTurn/DashTurnBack (veja a próxima seção).

Configuração do Animator

O PlayerAnimationStatesController atualiza os parâmetros do seu Animator Controller a cada frame com base no estado de movimento/combate, e expõe métodos que você chama a partir de Animation Events para controlar as hitboxes. Adicione-o ao player junto com um componente Animator.

Parâmetros obrigatórios

ParâmetroTipoDefinido quando
IsRunningBoolInput horizontal está pressionado e o movimento não está desabilitado.
IsFallingBoolVelocidade vertical é negativa e o personagem está no ar.
IsGroundedBoolReflete CharacterGroundChecker.IsGrounded.
WantsJumpBoolVelocidade vertical é positiva e o personagem está no ar (subindo).
XVelocityFloatVelocidade horizontal absoluta — use para controlar um blend tree.
IsBufferedJumpBoolVerdadeiro enquanto um pulo está em buffer, esperando aterrissar.
Dash se usar dash—CharacterDashRoll chama Animator.Play("Player-Roll") diretamente — nomeie seu estado de rolada como "Player-Roll", ou altere a string em CharacterDashRoll.ExecuteDash().
DashTurn / DashTurnBack se usar dashTriggerDisparado quando um input de virada interrompe o dash.
Player-Hit se usar PlayerHitSequence—PlayerHitSequence chama Animator.Play("Player-Hit") diretamente em todo hit efetivo recebido — nomeie seu estado de reação a hit como "Player-Hit", ou altere hitStateName no componente.

Ataques, virada e corrida não são controlados por parâmetros do tipo Trigger — PlayerAnimationStatesController chama Animator.Play(...) diretamente em um estado de relay para os ataques, e nos estados "Player-Turn" / "Player-TurnBack" / "Player-Run" para as mudanças de direção. Nomeie esses estados de acordo (ou atualize as chamadas correspondentes de Animator.StringToHash(...) no script, se preferir usar seus próprios nomes).

Estados de Ataque & Bloqueio de Movimento

CampoPadrãoO que faz
Attack State TagAttackA Tag do Animator (definida por estado, no Inspector do próprio estado, não é um parâmetro) que todo estado de ataque precisa ter. Usada para detectar "atacando atualmente", independente de qual passo do combo está tocando.
Attack Combo Accept Window0.85Progresso normalizado (0-1) do clipe de ataque atual a partir do qual um novo input de ataque pode interromper e avançar o combo. Antes disso, o input de ataque é ignorado para que o golpe atual sempre termine (ou alcance sua janela de cancelamento) primeiro.
O player não consegue se mover enquanto ataca Todo estado marcado com a Attack State Tag desabilita automaticamente o input de movimento horizontal do player no instante em que é iniciado, e o reabilita no instante em que é finalizado — ataques sempre travam o player no lugar. Marque apenas os estados de ataque dessa forma; marcar qualquer outra coisa (ex.: um estado de reação a hit ou de dash) também bloquearia o movimento enquanto esse estado estiver tocando.

Animation Events de Hitbox

PlayerAnimationStatesController expõe dois métodos genéricos — ActivateHitbox(string hitboxId) e DeactivateHitbox(string hitboxId) — em vez de um par de método fixo por ataque. Adicione um Animation Event em cada clip de ataque no frame em que a hitbox deve ativar, selecione ActivateHitbox na lista de funções, e digite o Id da hitbox (ex.: Punch01) no campo de parâmetro string do evento. Adicione um segundo evento no frame em que deve desativar, selecionando DeactivateHitbox com a mesma string de Id.

Por que um parâmetro string em vez de um método por ataque Animation Events conseguem carregar um único argumento string/float/int/Object, então um ActivateHitbox("Punch01") genérico funciona para qualquer ataque que você definir — inclusive os que você adicionar depois de comprar este pacote — sem nunca precisar tocar no código do PlayerAnimationStatesController. Adicione um novo filho CharacterHitbox com um novo Id, configure dois Animation Events com esse Id como parâmetro string, e está feito.

Essas strings de Id correspondem às strings Id de hitbox usadas em Combate & Hitboxes abaixo — é exatamente o mesmo valor, só digitado no campo de parâmetro do Animation Event em vez de um nome de método.

Combate & Hitboxes

1. Monte a hierarquia de hitboxes

Crie um GameObject filho por ataque sob o player, cada um com um componente CharacterHitbox e um ou mais shapes de Collider2D em modo trigger, dimensionados para esse ataque:

Player ├── Hitbox_Punch01 (BoxCollider2D, Is Trigger ✓) Id = "Punch01" ├── Hitbox_Punch02 (BoxCollider2D, Is Trigger ✓) Id = "Punch02" ├── Hitbox_Punch03 (BoxCollider2D, Is Trigger ✓) Id = "Punch03" └── Hitbox_Punch04 (2x BoxCollider2D, Is Trigger ✓) Id = "Punch04" ← múltiplos colliders, um ataque
CampoO que faz
IdString comparada com as chamadas de CharacterHitboxController.Activate(id) — precisa ser única por hitbox.
Target LayersLayerMask do que essa hitbox tem permissão para acertar (ex.: sua layer de Inimigos).

CharacterHitbox define automaticamente todos os seus colliders como isTrigger = true e começa desativada; você não precisa desabilitar os colliders manualmente.

2. Adicione o controller e o aplicador de dano

Na raiz do player (mesmo GameObject que PlayerController), adicione:

ComponenteFinalidade
CharacterHitboxControllerColeta todos os CharacterHitbox filhos, expõe Activate/Deactivate por Id, e espelha os offsets X das hitboxes quando o player vira.
MeleeDamageDealerEscuta os hits e aplica dano/knockback no que a hitbox tocou.

CharacterHitboxController coleta automaticamente seus filhos CharacterHitbox se o array for deixado vazio no Inspector — você só precisa arrastá-los manualmente se quiser um subconjunto específico.

3. Configure o dano por ataque

Em MeleeDamageDealer, defina um dano/knockback/tipo padrão, e opcionalmente adicione overrides por hitbox:

CampoO que faz
Default DamageUsado para qualquer Id de hitbox sem uma entrada explícita abaixo.
Default KnockbackVector2. X = força horizontal (sempre positiva — a direção é resolvida automaticamente a partir das posições de atacante/alvo). Y = força vertical, positivo = para cima.
Default Damage TypePhysical / Magic / Fire — estenda o enum DamageType em Core/DamageInfo.cs se precisar de mais.
Hitbox ConfigsArray de overrides: HitboxId (deve corresponder exatamente ao CharacterHitbox.Id), Damage, Knockback, DamageType.
Exemplos de valores de knockback (5, 0) — empurrão puramente horizontal. (3, 8) — lança o alvo para cima e para o lado. (0, 10) — uppercut direto.

4. Torne os alvos capazes de receber dano

Qualquer GameObject que as hitboxes devam afetar precisa de um HealthComponent (ou seu próprio componente implementando IDamageable) e, idealmente, um Rigidbody2D se você quiser que o knockback se aplique (HealthComponent já implementa IKnockbackable).

Pipeline completo de hit

CharacterHitbox (OnTriggerEnter2D)
   → CharacterHitboxController.OnAnyHit
       → MeleeDamageDealer.OnAnyHit
           → IDamageable.TakeDamage(DamageInfo)      no alvo
           → IKnockbackable.ApplyKnockback(...)      no alvo, se implementado
           → CharacterHitboxController.NotifyHitProcessed(...)
               → qualquer coisa escutando OnHitProcessed (PlayerHitCounter, HitConnectEffect, ...)

Vida & Dano

Adicione HealthComponent ao player e a qualquer inimigo/destrutível que deva poder receber dano.

CampoPadrãoO que faz
Max Health100Valor de vida inicial e máximo.
Destroy On DeathtrueDestrói automaticamente o GameObject quando a vida chega a zero. Desative para o player se você cuidar de morte/respawn por conta própria.

Reagindo a eventos de vida

Outros sistemas devem assinar eventos em vez de fazer polling — por exemplo, uma UI de barra de vida:

void Start()
{
    var health = player.GetComponent<HealthComponent>();
    health.OnHealthChanged += (current, max) => healthBar.fillAmount = current / max;
    health.OnDied += info => PlayDeathSequence();
}

Opcional: combo / sequência de hit no player

ComponenteAdiciona
PlayerHitSequenceToca a animação de reação "Player-Hit" em todo hit efetivo (veja os parâmetros do Animator acima).
PlayerHitCounterRastreia uma sequência de hits consecutivos para uma UI de combo/score. Reseta por timeout ou quando o player recebe dano.

Invulnerabilidade & defesa (pontos de extensão)

HealthComponent.TakeDamage verifica automaticamente um componente IVariableVulnerability (ex.: PlayerController, que já implementa via Invulnerable()/Vulnerable()) e um componente IDefendable no mesmo GameObject, se presente. Chame PlayerController.Invulnerable() durante uma janela de dash ou hit-stun para tornar o player imune a dano, depois Vulnerable() para restaurar. Veja Estendendo: Interfaces para saber como adicionar seu próprio componente de bloqueio/parry via IDefendable.

Feedback de Hit (FX) opcional

Esses componentes adicionam "game feel" por cima do pipeline de combate acima. Nenhum deles é obrigatório para o combate funcionar — eles apenas reagem a eventos que já são disparados.

CameraShake2D

Um pequeno tremor autocontido — sem necessidade de Cinemachine ou outro pacote de câmera. Adicione à Transform que deve realmente se mover (sua Camera, ou a Transform de uma virtual camera do Cinemachine, caso você o utilize).

CampoO que faz
TargetTransform a ser tremida. Usa o próprio GameObject por padrão se deixado vazio.

Chame cameraShake.Shake(amplitude, duration) você mesmo, ou atribua-o nos campos abaixo para que os efeitos incluídos o acionem automaticamente.

HitImpactEffect player recebe dano

Adicione ao mesmo GameObject que HealthComponent (o player). Toca um breve slow motion mais um tremor de câmera sempre que o player recebe dano efetivo.

CampoPadrão
Slow Time Scale0.15
Slow Duration / Slow Recovery0.08s / 0.35s
Shake Amplitude / Shake Recovery3 / 0.4s
Camera ShakeArraste seu CameraShake2D aqui. Deixe vazio para pular o tremor.

HitConnectEffect ataque do player acerta

Adicione ao mesmo GameObject que CharacterHitboxController (o player). Mais sutil que o HitImpactEffect já que dispara com muito mais frequência — um hit-stop curto mais um pequeno tremor, e prefabs de VFX opcionais.

CampoPadrão
Hit Stop Time Scale / Duration0.05 / 0.05s
Shake Amplitude / Recovery1.2 / 0.15s
Hit VFX PrefabInstanciado na posição do alvo em um hit normal.
Blocked Hit VFX PrefabInstanciado em vez do anterior se o alvo se defendeu (veja IDefendable).

PlayerReceiveHitEffect player recebe dano, perspectiva do receptor

Adicione ao player junto com HealthComponent. Spawna um prefab de VFX em (ou perto de) o player quando ele recebe dano — diferente do HitConnectEffect, que é a perspectiva do atacante.

CampoO que faz
Hit VFX PrefabInstanciado em dano efetivo, orientado na direção oposta ao golpe (um efeito de "respingo").
Spawn OffsetOffset local em relação à posição do player — use para alinhar com o torso.
O que foi intencionalmente removido Os efeitos originais nos quais este pacote se baseia também acionavam um override de Vignette/Chromatic Aberration de um Volume URP e um tremor baseado em ruído do Cinemachine. Para manter este pacote livre de dependências, o tremor do Cinemachine foi substituído pelo CameraShake2D autocontido acima, e os overrides de Volume URP foram removidos em vez de reimplementados (exigiriam um profile de Volume específico vinculado ao URP). Se você usa URP e Cinemachine, pode adicioná-los de volta assinando seu próprio script a HealthComponent.OnDamaged / CharacterHitboxController.OnHitProcessed — veja a Tabela de Eventos.

Referência de Scripts

Core

ScriptTipoResumo
DamageInfostructQuantidade, origem, tipo, direção e knockback de um único golpe.
IDamageableinterfaceCurrentHealth / MaxHealth / IsDead / TakeDamage(DamageInfo).
IKnockbackableinterfaceApplyKnockback(direction, knockback).
IDefendableinterfaceTryDefend(DamageInfo) → DefenseResult. Implemente para seu próprio bloqueio/parry.
IVariableVulnerabilityinterfaceIsInvulnerable — verificado pelo HealthComponent antes de aplicar dano.
IFacingDirectionProviderinterfaceFacingDir (+1/-1). Implementado por PlayerController; lido pelo CharacterHitboxController para espelhar as hitboxes ao virar — implemente em seu próprio tipo de personagem para usar o sistema de hitboxes sem depender de PlayerController.

Movement

ScriptResumo
CharacterMovementAceleração/desaceleração horizontal, multiplicadores de velocidade/controle aéreo, e a API de força externa (ApplyConstantExternalForce, FreezeInAir, etc.) usada por pulo, dash e knockback.
CharacterGroundCheckerDetecção de chão com coyote time e troca dinâmica de friction.
CharacterJumpPulo com buffer e altura variável. Classe base — herde dela para personagens que não sejam o player.
PlayerJumpSubclasse de CharacterJump voltada para o player.
CharacterDashRollDash com buffer e interrupção por virada.
CharacterInputControllerPonte "Send Messages" do Input System: OnMove / OnJump / OnDash / OnAttack.

Player

ScriptResumo
PlayerControllerAgrega o rig, rastreia o estado de combo (AttackCombo), flags de ataque/defesa, direção de frente, invulnerabilidade. O principal ponto de referência para outros scripts.
PlayerHitCounterSequência de hits consecutivos com timeout, para UI.

Combat

ScriptResumo
CharacterHitboxCollider(s) de trigger de um ataque + evento OnHit.
CharacterHitboxControllerActivate/Deactivate por Id, eventos OnAnyHit / OnHitProcessed, espelhamento ao virar.
MeleeDamageDealerResolve dano/knockback por Id de hitbox e aplica via IDamageable/IKnockbackable.
HealthComponentVida, resolução de dano (verificações de defesa + invulnerabilidade), knockback, eventos de morte/cura.
PlayerHitSequenceToca o estado de Animator "Player-Hit" em todo hit efetivo recebido.

Combat/FX

ScriptResumo
CameraShake2DTremor de câmera autocontido com ruído decrescente.
HitImpactEffectSlow motion + tremor quando o player recebe dano.
HitConnectEffectHit-stop + tremor + VFX quando o ataque do player acerta.
PlayerReceiveHitEffectSpawn de VFX quando o player recebe dano.

Animations

ScriptResumo
PlayerAnimationStatesControllerAtualiza parâmetros do Animator a cada frame, expõe métodos Activate/Deactivate de hitbox para Animation Events.

Estendendo: Interfaces

O sistema de combate é construído em torno de pequenas interfaces em Core/ para que você possa adicionar novos tipos de entidade (inimigos, destrutíveis, escudos) sem tocar nos scripts do pacote.

Tornar algo capaz de receber dano sem usar HealthComponent

using CloudedStudio.CharacterPack2D.Core;

public class BreakableCrate : MonoBehaviour, IDamageable
{
    [SerializeField] private float maxHealth = 20f;
    private float current;

    public float CurrentHealth => current;
    public float MaxHealth => maxHealth;
    public bool IsDead => current <= 0f;

    private void Awake() => current = maxHealth;

    public float TakeDamage(DamageInfo info)
    {
        float applied = Mathf.Min(info.Amount, current);
        current -= applied;
        if (IsDead) Destroy(gameObject);
        return applied;
    }
}

Adicionar um componente de bloqueio/parry

Implemente IDefendable no mesmo GameObject que HealthComponent — ele será consultado automaticamente antes do dano ser aplicado:

using CloudedStudio.CharacterPack2D.Core;

public class ShieldBlock : MonoBehaviour, IDefendable
{
    public bool IsBlocking;
    [Range(0f, 1f)] public float DamageReduction = 1f; // 1 = bloqueio total

    public DefenseResult TryDefend(DamageInfo info)
    {
        return IsBlocking
            ? DefenseResult.Success(info.Amount, DamageReduction)
            : DefenseResult.Failed(info.Amount);
    }
}

Invulnerabilidade customizada

PlayerController já implementa IVariableVulnerability. Para uma entidade que não seja o player, implemente você mesmo:

using CloudedStudio.CharacterPack2D.Core;

public class EnemyController : MonoBehaviour, IVariableVulnerability
{
    public bool IsInvulnerable { get; private set; }
    public void SetInvulnerable(bool value) => IsInvulnerable = value;
}

Tabela de Eventos

EventoDisparado porAssinaturaDispara quando
OnDirectionChangedCharacterMovement(Vector2 newInput, Vector2 previousInput)O vetor de input de movimento muda.
OnGroundedChangedCharacterGroundChecker(GroundChangedEvent)O estado de "no chão" se inverte.
OnAnyHitCharacterHitboxController(string hitboxId, GameObject target)Uma hitbox sobrepõe um alvo válido, antes do dano ser resolvido.
OnHitProcessedCharacterHitboxController(string hitboxId, GameObject target, bool wasDefended)Depois que o dano foi resolvido para aquele hit.
OnDamagedHealthComponent(DamageInfo info, float effectiveDamage)Dano é aplicado (effectiveDamage pode ser 0 se totalmente bloqueado).
OnDiedHealthComponent(DamageInfo info)A vida chega a zero.
OnHealthChangedHealthComponent(float current, float max)A vida muda por qualquer motivo (dano ou Heal()).
OnCountChanged / OnTimerChanged / OnResetPlayerHitCounter(int) / (float) / ()A contagem de hits muda / a cada frame enquanto ativo / a sequência reseta.

Solução de Problemas

O player não pula, ou pula de forma inconsistente.

Verifique se Ground Layer no CharacterGroundChecker inclui suas layers de chão/plataforma, e se o Collider2D do player não está marcado como trigger. Se o player ficar preso em paredes durante um pulo, garanta que ambos os campos de PhysicsMaterial2D estão atribuídos e que Dynamic Friction está ativado.

Os métodos do CharacterInputController nunca são chamados.

Confirme que o Behavior do componente PlayerInput está definido como Send Messages, e que as ações do seu action map estão nomeadas literalmente como Move, Jump, Dash, Attack (o PlayerInput deriva o nome da mensagem — OnMove, etc. — a partir do nome da ação).

O pulo sempre atinge a mesma altura, mesmo se eu soltar o botão rapidamente.

Isso significa que OnJump só está recebendo o press, nunca o release. Confirme com um teste rápido: adicione temporariamente Debug.Log(value.isPressed) dentro de CharacterInputController.OnJump — se o Console só mostrar True e nunca False, a action Jump não tem nenhuma Interaction atribuída, o que usa por padrão uma Interaction implícita Press no modo Press Only (sem fase Canceled no release). Adicione uma Interaction Press à action Jump e defina seu Trigger Behavior como Press and Release — veja o aviso em Configuração de Input.

As hitboxes nunca causam dano.

Verifique, em ordem: o Target Layers da hitbox inclui a layer do alvo; o alvo tem um Rigidbody2D ou o collider da hitbox consegue resolver o GameObject raiz do alvo diretamente; o alvo tem um componente implementando IDamageable (ex.: HealthComponent); o Animation Event está realmente chamando Activate("SeuId") no frame correto (avance o clip na janela de Animation para verificar).

Parâmetros do Animator geram avisos de "parameter does not exist".

Adicione todo parâmetro listado em Configuração do Animator ao seu Animator Controller — PlayerAnimationStatesController os define a cada frame independentemente de sua state machine usá-los atualmente.

O dano não é reduzido mesmo tendo anexado um componente de defesa/invulnerabilidade.

HealthComponent busca IDefendable / IVariableVulnerability via GetComponent no próprio GameObject — garanta que seu componente customizado está no mesmo GameObject que HealthComponent, não em um filho ou pai.

Time.timeScale fica travado abaixo de 1.

Isso pode acontecer se um GameObject com HitImpactEffect ou HitConnectEffect for destruído no meio do efeito fora do fluxo normal (ex.: parar o Play Mode enquanto uma coroutine está em execução). Ambos os scripts resetam Time.timeScale em OnDestroy, mas se você notar isso travado depois de uma parada abrupta, basta entrar e sair do Play Mode uma vez.

Perguntas Frequentes

Preciso do pacote Input System?

Apenas se você usar o CharacterInputController. Todos os outros scripts reagem a chamadas de método simples (Jump(), TriggerDash(), SetMovementInput(...), SetAttack()) — conecte-as a partir do Input Manager legado ou da sua própria solução de input, se preferir.

Posso usar isto para um inimigo em vez do player?

Os scripts de movimento/pulo/dash/combate não são exclusivos do player, exceto PlayerController, PlayerJump, PlayerHitCounter, PlayerHitSequence e PlayerAnimationStatesController, que assumem um único personagem controlado. Para um inimigo, controle CharacterMovement/CharacterJump/CharacterHitboxController/MeleeDamageDealer diretamente a partir do seu script de IA, em vez de via PlayerController.

Como adiciono um 5º ataque de combo?

Aumente o Combo Length em PlayerController, adicione o novo filho CharacterHitbox + estado de Animator + Animation Events, e adicione um par de métodos Activate/Deactivate correspondente em PlayerAnimationStatesController seguindo o padrão existente.

Por que não há sistema de target-lock / avanço até um inimigo?

Isso está intencionalmente fora do escopo deste pacote — é um controlador de personagem 2D de uso geral, não vinculado a nenhum design de targeting específico. Construa seu próprio sistema de targeting sobre o pipeline do MeleeDamageDealer se o seu jogo precisar disso.

Changelog

VersãoNotas
1.0Lançamento inicial: movimento, pulo, dash, combate corpo-a-corpo com combo, vida/dano, reações de hit, FX de feedback de impacto.