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.
Conteúdo do Pacote
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.
- Crie um GameObject de player com um
Rigidbody2D(Body Type: Dynamic, Gravity Scale ~3-5, Freeze Rotation Z) e umCollider2Dsem trigger para o corpo. - Adicione CharacterGroundChecker, CharacterMovement, PlayerJump e PlayerController — veja Movimento & Pulo.
- Adicione um componente
PlayerInput(pacote Input System) e CharacterInputController — veja Configuração de Input. - Adicione um
Animatorcom os parâmetros exigidos e PlayerAnimationStatesController — veja Configuração do Animator. - (Opcional) Adicione CharacterDashRoll para ter dash — veja Dash / Rolada.
- Construa as hitboxes de ataque como objetos filhos e adicione CharacterHitboxController + MeleeDamageDealer — veja Combate & Hitboxes.
- Adicione HealthComponent ao player (e a qualquer outra coisa que deva poder receber dano) — veja Vida & Dano.
- (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ção | Teclas |
|---|---|
| Mover | Setas do teclado (Cima / Baixo / Esquerda / Direita) |
| Pular | Z ou Espaço |
| Atacar | X |
| Dash | C |
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
| Componente | Finalidade |
|---|---|
Rigidbody2D | Exigido por tudo abaixo. Defina Body Type como Dynamic e congele a rotação em Z. |
CharacterGroundChecker | Detecta contato com o chão via Physics2D, com buffer de coyote time. |
CharacterMovement | Aceleração/desaceleração horizontal e a API de "força externa" usada por pulo/dash/knockback. |
PlayerJump | Pulo com buffer e altura variável (cortado ao soltar o botão). |
PlayerController | Agrega 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
| Campo | O que faz |
|---|---|
| Ground Layer | LayerMask do que conta como "chão" — defina para suas layers de Chão/Plataforma. |
| Dynamic Friction | Deixe 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 2D | Um PhysicsMaterial2D com friction baixo/zero. Crie um via Assets > Create > 2D > Physics Material 2D. |
| Grounded Physics Material 2D | Seu material de friction normal de gameplay (pode ser o mesmo que já está no Rigidbody2D). |
Campos do CharacterMovement
| Campo | Padrão | O que faz |
|---|---|---|
| Initial Speed | 2 | Velocidade para a qual o personagem é "encaixado" instantaneamente ao começar a se mover do zero. |
| Max Speed | 8 | Velocidade horizontal máxima. |
| Acceleration | 30 | Unidades/segundo² aplicadas enquanto o personagem se move em direção à velocidade alvo. |
| Deceleration Factor | 1.5 | Multiplicador sobre Acceleration usado ao parar (sem input pressionado). |
Campos do PlayerJump / CharacterJump
| Campo | Padrão | O que faz |
|---|---|---|
| Buffer Jump Window | 0.15s | Por quanto tempo um pulo pressionado um pouco antes da hora é lembrado, executando assim que as condições de chão/coyote permitirem. |
| Jump Force | 16.5 | Impulso de velocidade para cima aplicado no pulo. |
| Cutting Factor | 0.5 | Multiplica a velocidade vertical restante quando o botão de pulo é solto antes da hora — gera altura de pulo variável. |
| Air Control Factor | 0.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.
- Adicione um componente
PlayerInput(do pacote Input System) ao GameObject do player. - Defina seu Behavior como
Send Messages. -
Atribua (ou crie) um Input Actions asset com um action map
Playercontendo estas ações:Nome da ação Tipo Binding 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 DashButton Shift Esquerdo / Botão Leste AttackButton Botão Esquerdo do Mouse / Botão Oeste - Adicione CharacterInputController ao mesmo GameObject. Seus campos de referência se preenchem automaticamente via
GetComponentno Reset se deixados vazios. Defina Action Map Name para corresponder ao action map criado acima (padrão:"Player").
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.
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.
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.
| Campo | Padrão | O que faz |
|---|---|---|
| Dash Speed Multiplier | 2 | Velocidade do dash = CharacterMovement.MaxSpeed × este valor. |
| Dash Duration | 0.3s | Quanto tempo o dash dura antes do controle voltar ao movimento normal. |
| Buffer Dash Window | 0.15s | Se Dash for pressionado no ar, o input é lembrado por esse tempo — aterrissar dentro da janela executa o dash automaticamente. |
| Player ref | auto | Usado 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âmetro | Tipo | Definido quando |
|---|---|---|
IsRunning | Bool | Input horizontal está pressionado e o movimento não está desabilitado. |
IsFalling | Bool | Velocidade vertical é negativa e o personagem está no ar. |
IsGrounded | Bool | Reflete CharacterGroundChecker.IsGrounded. |
WantsJump | Bool | Velocidade vertical é positiva e o personagem está no ar (subindo). |
XVelocity | Float | Velocidade horizontal absoluta — use para controlar um blend tree. |
IsBufferedJump | Bool | Verdadeiro 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 dash | Trigger | Disparado 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
| Campo | Padrão | O que faz |
|---|---|---|
| Attack State Tag | Attack | A 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 Window | 0.85 | Progresso 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. |
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.
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:
| Campo | O que faz |
|---|---|
| Id | String comparada com as chamadas de CharacterHitboxController.Activate(id) — precisa ser única por hitbox. |
| Target Layers | LayerMask 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:
| Componente | Finalidade |
|---|---|
CharacterHitboxController | Coleta todos os CharacterHitbox filhos, expõe Activate/Deactivate por Id, e espelha os offsets X das hitboxes quando o player vira. |
MeleeDamageDealer | Escuta 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:
| Campo | O que faz |
|---|---|
| Default Damage | Usado para qualquer Id de hitbox sem uma entrada explícita abaixo. |
| Default Knockback | Vector2. 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 Type | Physical / Magic / Fire — estenda o enum DamageType em Core/DamageInfo.cs se precisar de mais. |
| Hitbox Configs | Array de overrides: HitboxId (deve corresponder exatamente ao CharacterHitbox.Id), Damage, Knockback, DamageType. |
(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.
| Campo | Padrão | O que faz |
|---|---|---|
| Max Health | 100 | Valor de vida inicial e máximo. |
| Destroy On Death | true | Destró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
| Componente | Adiciona |
|---|---|
PlayerHitSequence | Toca a animação de reação "Player-Hit" em todo hit efetivo (veja os parâmetros do Animator acima). |
PlayerHitCounter | Rastreia 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).
| Campo | O que faz |
|---|---|
| Target | Transform 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.
| Campo | Padrão |
|---|---|
| Slow Time Scale | 0.15 |
| Slow Duration / Slow Recovery | 0.08s / 0.35s |
| Shake Amplitude / Shake Recovery | 3 / 0.4s |
| Camera Shake | Arraste 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.
| Campo | Padrão |
|---|---|
| Hit Stop Time Scale / Duration | 0.05 / 0.05s |
| Shake Amplitude / Recovery | 1.2 / 0.15s |
| Hit VFX Prefab | Instanciado na posição do alvo em um hit normal. |
| Blocked Hit VFX Prefab | Instanciado 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.
| Campo | O que faz |
|---|---|
| Hit VFX Prefab | Instanciado em dano efetivo, orientado na direção oposta ao golpe (um efeito de "respingo"). |
| Spawn Offset | Offset local em relação à posição do player — use para alinhar com o torso. |
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
| Script | Tipo | Resumo |
|---|---|---|
| DamageInfo | struct | Quantidade, origem, tipo, direção e knockback de um único golpe. |
| IDamageable | interface | CurrentHealth / MaxHealth / IsDead / TakeDamage(DamageInfo). |
| IKnockbackable | interface | ApplyKnockback(direction, knockback). |
| IDefendable | interface | TryDefend(DamageInfo) → DefenseResult. Implemente para seu próprio bloqueio/parry. |
| IVariableVulnerability | interface | IsInvulnerable — verificado pelo HealthComponent antes de aplicar dano. |
| IFacingDirectionProvider | interface | FacingDir (+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
| Script | Resumo |
|---|---|
| CharacterMovement | Aceleraçã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. |
| CharacterGroundChecker | Detecção de chão com coyote time e troca dinâmica de friction. |
| CharacterJump | Pulo com buffer e altura variável. Classe base — herde dela para personagens que não sejam o player. |
| PlayerJump | Subclasse de CharacterJump voltada para o player. |
| CharacterDashRoll | Dash com buffer e interrupção por virada. |
| CharacterInputController | Ponte "Send Messages" do Input System: OnMove / OnJump / OnDash / OnAttack. |
Player
| Script | Resumo |
|---|---|
| PlayerController | Agrega 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. |
| PlayerHitCounter | Sequência de hits consecutivos com timeout, para UI. |
Combat
| Script | Resumo |
|---|---|
| CharacterHitbox | Collider(s) de trigger de um ataque + evento OnHit. |
| CharacterHitboxController | Activate/Deactivate por Id, eventos OnAnyHit / OnHitProcessed, espelhamento ao virar. |
| MeleeDamageDealer | Resolve dano/knockback por Id de hitbox e aplica via IDamageable/IKnockbackable. |
| HealthComponent | Vida, resolução de dano (verificações de defesa + invulnerabilidade), knockback, eventos de morte/cura. |
| PlayerHitSequence | Toca o estado de Animator "Player-Hit" em todo hit efetivo recebido. |
Combat/FX
| Script | Resumo |
|---|---|
| CameraShake2D | Tremor de câmera autocontido com ruído decrescente. |
| HitImpactEffect | Slow motion + tremor quando o player recebe dano. |
| HitConnectEffect | Hit-stop + tremor + VFX quando o ataque do player acerta. |
| PlayerReceiveHitEffect | Spawn de VFX quando o player recebe dano. |
Animations
| Script | Resumo |
|---|---|
| PlayerAnimationStatesController | Atualiza 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
| Evento | Disparado por | Assinatura | Dispara quando |
|---|---|---|---|
| OnDirectionChanged | CharacterMovement | (Vector2 newInput, Vector2 previousInput) | O vetor de input de movimento muda. |
| OnGroundedChanged | CharacterGroundChecker | (GroundChangedEvent) | O estado de "no chão" se inverte. |
| OnAnyHit | CharacterHitboxController | (string hitboxId, GameObject target) | Uma hitbox sobrepõe um alvo válido, antes do dano ser resolvido. |
| OnHitProcessed | CharacterHitboxController | (string hitboxId, GameObject target, bool wasDefended) | Depois que o dano foi resolvido para aquele hit. |
| OnDamaged | HealthComponent | (DamageInfo info, float effectiveDamage) | Dano é aplicado (effectiveDamage pode ser 0 se totalmente bloqueado). |
| OnDied | HealthComponent | (DamageInfo info) | A vida chega a zero. |
| OnHealthChanged | HealthComponent | (float current, float max) | A vida muda por qualquer motivo (dano ou Heal()). |
| OnCountChanged / OnTimerChanged / OnReset | PlayerHitCounter | (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ão | Notas |
|---|---|
| 1.0 | Lançamento inicial: movimento, pulo, dash, combate corpo-a-corpo com combo, vida/dano, reações de hit, FX de feedback de impacto. |