Sistema de Animação
Introdução
DayZ utiliza um sistema de animação baseado em máquina de estados integrado ao motor Enfusion. As animações do jogador são controladas por uma hierarquia de classes HumanCommand -- movimento, ações, escalada, natação, veículos, queda, morte e inconsciência, cada um com seu próprio comando dedicado. Animações de objetos (portas, tampas, construções implantáveis) são controladas por AnimationSources no model.cfg e gerenciadas via script com SetAnimationPhase().
Este capítulo cobre a API completa de animação: a máquina de estados de movimento do jogador, o sistema de comandos humanos, o pipeline de gestos/emotes, fontes de animação de objetos, callbacks de ação com eventos de animação, e as constantes-chave de DayZPlayerConstants com as quais modders interagem diariamente. Todas as assinaturas de métodos e constantes foram extraídas diretamente do código-fonte vanilla.
Máquina de Estados de Movimento do Jogador
Transições de Postura
HumanMovementState
O motor expõe o estado atual de animação do jogador através de HumanMovementState. Recupere-o chamando GetMovementState() em qualquer Human (ou subclasse):
// Fonte: scripts/3_game/human.c
class HumanMovementState
{
int m_CommandTypeId; // ID do comando atual (COMMANDID_MOVE, COMMANDID_ACTION, etc.)
int m_iStanceIdx; // postura atual (STANCEIDX_ERECT, STANCEIDX_CROUCH, etc.)
int m_iMovement; // 0=parado, 1=andando, 2=correndo, 3=sprint
float m_fLeaning; // deslocamento de inclinação, 0 quando não inclinado
bool IsRaised(); // true quando postura >= STANCEIDX_RAISEDERECT
bool IsRaisedInProne(); // true quando STANCEIDX_RAISEDPRONE
bool IsInProne(); // true quando STANCEIDX_PRONE
bool IsInRaisedProne(); // true quando STANCEIDX_RAISEDPRONE
bool IsLeaning(); // true quando m_fLeaning != 0
}Padrão de uso:
HumanMovementState state = new HumanMovementState();
player.GetMovementState(state);
if (state.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_PRONE)
{
// jogador está deitado
}
if (state.m_iMovement >= 2)
{
// jogador está correndo ou em sprint
}Índices de Postura
Essas constantes identificam a postura corporal atual do jogador. Definidas em DayZPlayerConstants (scripts/3_game/dayzplayer.c):
| Constante | Valor | Descrição |
|---|---|---|
STANCEIDX_ERECT | 0 | Em pé |
STANCEIDX_CROUCH | 1 | Agachado |
STANCEIDX_PRONE | 2 | Deitado |
STANCEIDX_RAISEDERECT | 3 | Em pé com arma levantada |
STANCEIDX_RAISEDCROUCH | 4 | Agachado com arma levantada |
STANCEIDX_RAISEDPRONE | 5 | Deitado com arma levantada |
STANCEIDX_RAISED | 3 | Offset -- adicione à postura base para obter a variante levantada |
A relação: STANCEIDX_ERECT + STANCEIDX_RAISED = STANCEIDX_RAISEDERECT.
Máscaras de Postura
Flags de bitmask usadas por IsPlayerInStance() e StartCommand_Action() para especificar quais posturas uma animação suporta:
| Constante | Descrição |
|---|---|
STANCEMASK_ERECT | Em pé |
STANCEMASK_CROUCH | Agachado |
STANCEMASK_PRONE | Deitado |
STANCEMASK_RAISEDERECT | Em pé levantado |
STANCEMASK_RAISEDCROUCH | Agachado levantado |
STANCEMASK_RAISEDPRONE | Deitado levantado |
STANCEMASK_ALL | Todas as posturas combinadas |
STANCEMASK_NOTRAISED | ERECT | CROUCH | PRONE |
STANCEMASK_RAISED | RAISEDERECT | RAISEDCROUCH | RAISEDPRONE |
// Método de DayZPlayer:
proto native bool IsPlayerInStance(int pStanceMask);
// Exemplo: verificar se está em pé ou agachado (sem arma levantada)
if (player.IsPlayerInStance(DayZPlayerConstants.STANCEMASK_ERECT | DayZPlayerConstants.STANCEMASK_CROUCH))
{
// jogador está em pé ou agachado, arma abaixada
}Índices de Movimento
| Constante | Valor | Descrição |
|---|---|---|
MOVEMENTIDX_SLIDE | -2 | Deslizando |
MOVEMENTIDX_IDLE | 0 | Parado |
MOVEMENTIDX_WALK | 1 | Andando |
MOVEMENTIDX_RUN | 2 | Correndo |
MOVEMENTIDX_SPRINT | 3 | Em sprint |
MOVEMENTIDX_CROUCH_RUN | 4 | Correndo agachado |
O campo m_iMovement em HumanMovementState usa esses valores.
Sistema de Comandos Humanos
A qualquer momento, exatamente um comando principal controla o estado de animação do jogador. O motor fornece métodos getter que retornam null quando aquele comando não é o ativo. Apenas o comando atualmente ativo retorna um objeto válido.
Hierarquia de Comandos
| Getter | Classe | Descrição |
|---|---|---|
GetCommand_Move() | HumanCommandMove | Locomoção normal (parado, andando, correndo, sprint) |
GetCommand_Action() | HumanCommandActionCallback | Animações de ação de corpo inteiro |
GetCommand_Melee() | HumanCommandMelee | Corpo-a-corpo legado |
GetCommand_Melee2() | HumanCommandMelee2 | Sistema de corpo-a-corpo leve/pesado |
GetCommand_Fall() | HumanCommandFall | Queda/salto |
GetCommand_Ladder() | HumanCommandLadder | Subindo escadas |
GetCommand_Swim() | HumanCommandSwim | Nadando |
GetCommand_Vehicle() | HumanCommandVehicle | Sentado em veículo |
GetCommand_Climb() | HumanCommandClimb | Escalando obstáculos |
GetCommand_Death() | HumanCommandDeathCallback | Animação de morte |
GetCommand_Unconscious() | HumanCommandUnconscious | Estado inconsciente |
GetCommand_Damage() | HumanCommandFullBodyDamage | Reação de dano de corpo inteiro |
GetCommand_Script() | HumanCommandScript | Comando customizado totalmente scriptável |
Cada comando também possui um método StartCommand_*() correspondente na classe Human.
IDs de Comando
Cada tipo de comando possui um ID inteiro único armazenado em HumanMovementState.m_CommandTypeId. Também retornado por GetCurrentCommandID():
| Constante | Descrição |
|---|---|
COMMANDID_NONE | Nenhum comando (inválido) |
COMMANDID_MOVE | Movimento normal |
COMMANDID_ACTION | Ação de corpo inteiro |
COMMANDID_MELEE | Corpo-a-corpo (legado) |
COMMANDID_MELEE2 | Corpo-a-corpo leve/pesado |
COMMANDID_FALL | Queda |
COMMANDID_DEATH | Morto |
COMMANDID_DAMAGE | Dano de corpo inteiro |
COMMANDID_LADDER | Na escada |
COMMANDID_UNCONSCIOUS | Inconsciente |
COMMANDID_SWIM | Nadando |
COMMANDID_VEHICLE | Em veículo |
COMMANDID_CLIMB | Escalando |
COMMANDID_SCRIPT | Comando scriptado |
IDs de comando modificador (aditivos, sempre ativos):
| Constante | Descrição |
|---|---|
COMMANDID_MOD_LOOKAT | Direção do olhar da cabeça (sempre ativo) |
COMMANDID_MOD_WEAPONS | Manuseio de arma (sempre ativo) |
COMMANDID_MOD_ACTION | Sobreposição de ação aditiva |
COMMANDID_MOD_DAMAGE | Reação de dano aditiva |
HumanCommandMove
O comando de locomoção padrão. Métodos disponíveis:
class HumanCommandMove
{
proto native float GetCurrentMovementAngle(); // -180..180 graus
proto bool GetCurrentInputAngle(out float pAngle); // entrada bruta
proto native float GetCurrentMovementSpeed(); // 0=parado, 1=andando, 2=correndo, 3=sprint
proto native bool IsChangingStance();
proto native bool IsOnBack(); // deitado de costas
proto native bool IsInRoll(); // rolando
proto native bool IsLeavingUncon();
proto native void ForceStance(int pStanceIdx); // forçar postura, -1 para liberar
proto native void ForceStanceUp(int pStanceIdx); // forçar levantar
proto native void SetMeleeBlock(bool pBlock);
proto native void StartMeleeEvade();
}HumanCommandFall
class HumanCommandFall
{
static const int LANDTYPE_NONE = 0;
static const int LANDTYPE_LIGHT = 1;
static const int LANDTYPE_MEDIUM = 2;
static const int LANDTYPE_HEAVY = 3;
proto native bool PhysicsLanded(); // true quando tocou fisicamente o chão
proto native void Land(int pLandType);
proto native bool IsLanding(); // true durante a animação de aterrissagem
}HumanCommandVehicle
class HumanCommandVehicle
{
proto native Transport GetTransport();
proto native int GetVehicleClass(); // VEHICLECLASS_CAR, HELI, BOAT
proto native int GetVehicleSeat(); // VEHICLESEAT_DRIVER, CODRIVER, etc.
proto native void GetOutVehicle();
proto native void JumpOutVehicle();
proto native void SwitchSeat(int pTransportPositionIndex, int pVehicleSeat);
proto native bool IsGettingIn();
proto native bool IsGettingOut();
proto native bool IsSwitchSeat();
}HumanCommandClimb
class HumanCommandClimb
{
proto native int GetState(); // retorna valor do enum ClimbStates
proto native vector GetGrabPointWS();
proto native vector GetClimbOverStandPointWS();
// Testes estáticos -- use antes de iniciar uma escalada
proto native static bool DoClimbTest(Human pHuman, SHumanCommandClimbResult pResult, int pDebugDrawLevel);
proto native static bool DoPerformClimbTest(Human pHuman, SHumanCommandClimbResult pResult, int pDebugDrawLevel);
}
enum ClimbStates
{
STATE_MOVE,
STATE_TAKEOFF,
STATE_ONTOP,
STATE_FALLING,
STATE_FINISH
}HumanCommandUnconscious
class HumanCommandUnconscious
{
proto native void WakeUp(int targetStance = -1);
proto native bool IsWakingUp();
proto native bool IsOnLand();
proto native bool IsInWater();
}Sistema de Gestos / Emotes
O sistema de gestos do DayZ permite que os jogadores realizem animações sociais (acenar, apontar, sentar, dançar, render-se, suicídio, etc.). Ele é construído em três camadas: EmoteConstants (IDs), EmoteBase (configuração por emote) e EmoteManager (orquestração de reprodução).
EmoteConstants
Todos os IDs de emotes são definidos em EmoteConstants (scripts/3_game/constants.c):
| Constante | ID | Notas |
|---|---|---|
ID_EMOTE_GREETING | 1 | Acenar/saudação |
ID_EMOTE_SOS | 2 | Sinal de SOS de corpo inteiro |
ID_EMOTE_HEART | 3 | Gesto de coração |
ID_EMOTE_TAUNT | 4 | Provocação |
ID_EMOTE_LYINGDOWN | 5 | Deitar-se |
ID_EMOTE_TAUNTKISS | 6 | Provocação com beijo |
ID_EMOTE_FACEPALM | 7 | Facepalm |
ID_EMOTE_TAUNTELBOW | 8 | Provocação com cotovelo |
ID_EMOTE_THUMB | 9 | Polegar para cima |
ID_EMOTE_THROAT | 10 | Cortar a garganta |
ID_EMOTE_SUICIDE | 11 | Suicídio (corpo inteiro) |
ID_EMOTE_DANCE | 12 | Dançar |
ID_EMOTE_CAMPFIRE | 13 | Sentar perto da fogueira |
ID_EMOTE_SITA | 14 | Sentar variante A |
ID_EMOTE_SITB | 15 | Sentar variante B |
ID_EMOTE_THUMBDOWN | 16 | Polegar para baixo |
ID_EMOTE_DABBING | 32 | Dab |
ID_EMOTE_TIMEOUT | 35 | Sinal de tempo |
ID_EMOTE_CLAP | 39 | Aplaudir |
ID_EMOTE_POINT | 40 | Apontar para algo |
ID_EMOTE_SILENT | 43 | Gesto de silêncio |
ID_EMOTE_SALUTE | 44 | Saudação militar |
ID_EMOTE_RPS | 45 | Pedra-Papel-Tesoura |
ID_EMOTE_WATCHING | 46 | Gesto de observação |
ID_EMOTE_HOLD | 47 | Manter posição |
ID_EMOTE_LISTENING | 48 | Escutando |
ID_EMOTE_POINTSELF | 49 | Apontar para si mesmo |
ID_EMOTE_LOOKATME | 50 | Olhe para mim |
ID_EMOTE_TAUNTTHINK | 51 | Provocação pensativa |
ID_EMOTE_MOVE | 52 | Sinal de avançar |
ID_EMOTE_DOWN | 53 | Sinal de abaixar |
ID_EMOTE_COME | 54 | Venha aqui |
ID_EMOTE_NOD | 58 | Acenar com a cabeça (sim) |
ID_EMOTE_SHAKE | 59 | Balançar a cabeça (não) |
ID_EMOTE_SHRUG | 60 | Dar de ombros |
ID_EMOTE_SURRENDER | 61 | Render-se |
ID_EMOTE_VOMIT | 62 | Vomitar |
Classe EmoteBase
Cada emote é uma classe que estende EmoteBase (scripts/4_world/classes/emoteclasses/emotebase.c). Ela define requisitos de postura, IDs de callback de animação e condições opcionais:
class EmoteBase
{
protected int m_ID; // ID de EmoteConstants
protected string m_InputActionName; // nome da ação de entrada (ex.: "EmoteGreeting")
protected int m_StanceMaskAdditive; // posturas para reprodução aditiva (sobreposição)
protected int m_StanceMaskFullbody; // posturas para reprodução de corpo inteiro
protected int m_AdditiveCallbackUID; // constante CMD_GESTUREMOD_*
protected int m_FullbodyCallbackUID; // constante CMD_GESTUREFB_*
protected bool m_HideItemInHands; // esconder item nas mãos durante o emote
bool EmoteCondition(int stancemask); // sobrescreva para pré-condições customizadas
bool CanBeCanceledNormally(notnull EmoteCB callback);
bool EmoteFBStanceCheck(int stancemask); // valida postura de corpo inteiro
bool DetermineOverride(out int callback_ID, out int stancemask, out bool is_fullbody);
void OnBeforeStandardCallbackCreated(int callback_ID, int stancemask, bool is_fullbody);
void OnCallbackEnd();
bool EmoteStartOverride(typename callbacktype, int id, int mask, bool fullbody);
}Exemplo -- o emote de saudação suporta reprodução aditiva em pé/agachado e corpo inteiro deitado:
class EmoteGreeting extends EmoteBase
{
void EmoteGreeting()
{
m_ID = EmoteConstants.ID_EMOTE_GREETING;
m_InputActionName = "EmoteGreeting";
m_StanceMaskAdditive = DayZPlayerConstants.STANCEMASK_CROUCH | DayZPlayerConstants.STANCEMASK_ERECT;
m_StanceMaskFullbody = DayZPlayerConstants.STANCEMASK_PRONE;
m_AdditiveCallbackUID = DayZPlayerConstants.CMD_GESTUREMOD_GREETING;
m_FullbodyCallbackUID = DayZPlayerConstants.CMD_GESTUREFB_GREETING;
m_HideItemInHands = false;
}
}Alguns emotes requerem mãos vazias (dança, SOS, saudação, aplauso) via EmoteCondition:
class EmoteDance extends EmoteBase
{
void EmoteDance()
{
m_ID = EmoteConstants.ID_EMOTE_DANCE;
m_InputActionName = "EmoteDance";
m_StanceMaskAdditive = 0; // sem variante aditiva
m_StanceMaskFullbody = DayZPlayerConstants.STANCEMASK_ERECT;
m_FullbodyCallbackUID = DayZPlayerConstants.CMD_GESTUREFB_DANCE;
m_HideItemInHands = true;
}
override bool EmoteCondition(int stancemask)
{
if (m_Player.GetBrokenLegs() == eBrokenLegs.BROKEN_LEGS)
return false;
return !m_Player.GetItemInHands();
}
}Emotes Aditivos vs Corpo Inteiro
Emotes possuem dois modos de reprodução, selecionados automaticamente por EmoteManager.DetermineEmoteData():
- Aditivo (modificador): Sobreposto à locomoção. O jogador ainda pode se mover. Usa
AddCommandModifier_Action(). Ativado quando o jogador está em uma postura que corresponde am_StanceMaskAdditive. - Corpo inteiro: Assume todo o estado de animação. O jogador não pode se mover. Usa
StartCommand_Action(). Ativado quando o jogador está em uma postura que corresponde am_StanceMaskFullbody.
As constantes CMD_GESTUREMOD_* mapeiam para versões aditivas; constantes CMD_GESTUREFB_* mapeiam para versões de corpo inteiro.
EmoteManager
EmoteManager (scripts/4_world/classes/emotemanager.c) orquestra a reprodução de emotes. É criado por jogador e atualizado a cada frame pelo CommandHandler do jogador:
Responsabilidades principais:
- Registra todos os emotes via
EmoteConstructor.ConstructEmotes() - Detecta entrada de emote via
DetermineGestureIndex()(verifica keybinds) - Seleciona aditivo vs corpo inteiro baseado na postura atual
- Cria o callback
EmoteCBe gerencia seu ciclo de vida - Lida com condições de interrupção (entrada de movimento, profundidade da água, levantar arma)
- Gerencia o estado de rendição e a lógica do emote de suicídio
EmoteLauncher
EmoteLauncher é um objeto de requisição usado para enfileirar emotes via script (por exemplo, do menu de gestos ou forçado pelo servidor):
class EmoteLauncher
{
static const int FORCE_NONE = 0; // reprodução normal
static const int FORCE_DIFFERENT = 1; // forçar se diferente do atual
static const int FORCE_ALL = 2; // forçar sempre
void EmoteLauncher(int emoteID, bool interrupts_same);
void SetForced(int mode);
void SetStartGuaranteed(bool guaranteed); // permanece na fila até ser reproduzido
}Reproduzindo Emotes via Script
Para reproduzir um emote programaticamente, use o EmoteManager:
// Obter o gerenciador de emotes do jogador
EmoteManager emoteManager = player.GetEmoteManager();
// Criar um launcher para o emote de saudação
EmoteLauncher launcher = new EmoteLauncher(EmoteConstants.ID_EMOTE_GREETING, true);
launcher.SetForced(EmoteLauncher.FORCE_ALL);
// Enfileirar (o gerenciador processa no seu Update)
emoteManager.CreateEmoteCBFromMenu(EmoteConstants.ID_EMOTE_GREETING);Alternativamente, para controle direto no nível de ação (usado em ferramentas de câmera e debug):
// Gesto de corpo inteiro diretamente via StartCommand_Action
EmoteCB cb = EmoteCB.Cast(
player.StartCommand_Action(
DayZPlayerConstants.CMD_GESTUREFB_DANCE,
EmoteCB,
DayZPlayerConstants.STANCEMASK_ALL
)
);EmoteConstructor e Registro
EmoteConstructor (scripts/4_world/classes/emoteconstructor.c) registra todos os emotes vanilla. Modders podem sobrescrever RegisterEmotes() via modded class para adicionar entradas customizadas:
modded class EmoteConstructor
{
override void RegisterEmotes(TTypenameArray emotes)
{
super.RegisterEmotes(emotes);
emotes.Insert(MyCustomEmote);
}
}Animações de Objetos (model.cfg)
Objetos como portas, barris, tendas e construções implantáveis usam um sistema de animação separado definido no model.cfg. O script controla essas animações através de SetAnimationPhase().
API de Animação em Entity
Esses métodos são definidos em Entity (scripts/3_game/entities/entity.c) e estão disponíveis em toda entidade do jogo:
class Entity extends ObjectTyped
{
// Obter fase atual (0.0 a 1.0) de uma fonte de animação nomeada
proto native float GetAnimationPhase(string animation);
// Definir a fase alvo -- o motor interpola até ela
proto native void SetAnimationPhase(string animation, float phase);
// Definir fase imediatamente, sem interpolação
void SetAnimationPhaseNow(string animation, float phase);
// Resetar estado interno de animação
proto native void ResetAnimationPhase(string animation, float phase);
// Enumerar fontes de animação definidas pelo usuário
proto int GetNumUserAnimationSourceNames();
proto string GetUserAnimationSourceName(int index);
}AnimationSources no model.cfg
No model.cfg, cada modelo animado declara AnimationSources (os controladores) e Animations (o que eles movem). O campo source em uma entrada de animação a vincula a uma AnimationSource nomeada.
// Exemplo de model.cfg para um barril com tampa
class CfgModels
{
class MyBarrel
{
class AnimationSources
{
class Lid
{
source = "user"; // controlado por script
animPeriod = 0.5; // segundos para transição 0->1
initPhase = 0; // fase inicial
};
};
class Animations
{
class Lid_rot
{
type = "rotation";
source = "Lid"; // vincula à AnimationSource acima
selection = "lid"; // seleção nomeada no modelo
axis = "lid_axis"; // eixo do ponto de memória
minValue = 0;
maxValue = 1;
angle0 = 0; // radianos na fase 0
angle1 = 1.5708; // radianos na fase 1 (90 graus)
};
};
};
};Tipos de Fonte
| Tipo de Fonte | Descrição |
|---|---|
user | Controlado inteiramente por script via SetAnimationPhase() |
hit | Controlado pelo sistema de dano (animações de destruição) |
door | Controlado pelo sistema de abertura de portas |
reload | Ciclo de recarga de arma |
Para modding, user é o mais comum. Você o controla pelo Enforce Script.
Tipos de Animação no model.cfg
| Tipo | Descrição |
|---|---|
rotation | Rotaciona uma seleção ao redor de um eixo |
rotationX/Y/Z | Rotaciona ao redor de um eixo mundial específico |
translation | Translada uma seleção ao longo de um eixo |
translationX/Y/Z | Translada ao longo de um eixo mundial específico |
hide | Esconde/mostra uma seleção (baseado em limiar) |
Exemplo de Animação de Objeto Controlada por Script
Tampa de barril vanilla (scripts/4_world/entities/itembase/barrel_colorbase.c):
// Abrir o barril -- seleção Lid rotaciona, Lid2 esconde
SetAnimationPhase("Lid", 1); // rotaciona a tampa para aberta
SetAnimationPhase("Lid2", 0); // mostra a geometria do estado aberto
// Fechar o barril
SetAnimationPhase("Lid", 0); // rotaciona a tampa para fechada
SetAnimationPhase("Lid2", 1); // esconde a geometria do estado abertoPartes de construção de base (scripts/4_world/entities/itembase/basebuildingbase.c):
// Mostrar uma parte construída
SetAnimationPhase(ANIMATION_DEPLOYED, 0); // fase 0 = visível
// Escondê-la
SetAnimationPhase(ANIMATION_DEPLOYED, 1); // fase 1 = ocultoCallbacks de Ação
HumanCommandActionCallback
Todas as ações do jogador (comer, bandar, craftar, emotes) usam callbacks de animação. HumanCommandActionCallback (scripts/3_game/human.c) é a classe base:
class HumanCommandActionCallback
{
proto native Human GetHuman();
proto native void Cancel();
proto native void InternalCommand(int pInternalCommandId); // CMD_ACTIONINT_*
proto native void SetAligning(vector pPositionWS, vector pDirectionWS);
proto native void ResetAligning();
proto native void EnableCancelCondition(bool pEnable);
proto native bool DefaultCancelCondition();
proto native void RegisterAnimationEvent(string pAnimationEventStr, int pId);
proto native void EnableStateChangeCallback();
proto native int GetState(); // retorna constante STATE_*
// Sobrescritas de callback
void OnAnimationEvent(int pEventID);
void OnFinish(bool pCanceled);
void OnStateChange(int pOldState, int pCurrentState);
// Identificação de tipo
bool IsUserActionCallback();
bool IsGestureCallback();
bool IsSymptomCallback();
}Estados de Ação
Ações passam por estados definidos acessíveis via GetState():
| Constante | Valor | Descrição |
|---|---|---|
STATE_NONE | 0 | Não executando |
STATE_LOOP_IN | 1 | Entrando no loop |
STATE_LOOP_LOOP | 2 | No loop principal |
STATE_LOOP_END | 3 | Saindo do loop (fim primário) |
STATE_LOOP_END2 | 4 | Saindo do loop (fim secundário) |
STATE_LOOP_LOOP2 | 5 | Loop secundário |
STATE_LOOP_ACTION | 6 | Ação dentro do loop |
STATE_NORMAL | 7 | Ação única (sem loop) |
Comandos Internos de Ação
Use InternalCommand() para controlar o fluxo da ação:
| Constante | Valor | Descrição |
|---|---|---|
CMD_ACTIONINT_INTERRUPT | -2 | Cancelamento forçado, sem animação de saída |
CMD_ACTIONINT_FINISH | -1 | Finalização secundária (ex.: ficou sem água) |
CMD_ACTIONINT_END | 0 | Fim normal (todas as ações suportam) |
CMD_ACTIONINT_ACTION | 1 | Disparar ação secundária (ex.: alternância de polegar para cima/baixo) |
CMD_ACTIONINT_ACTIONLOOP | 2 | Loop de ação secundária |
Registrando Eventos de Animação
Eventos de animação são gatilhos nomeados embutidos em arquivos de animação. Registre-os para receber callbacks:
class EmoteCB extends HumanCommandActionCallback
{
override void OnAnimationEvent(int pEventID)
{
switch (pEventID)
{
case EmoteConstants.EMOTE_SUICIDE_DEATH:
if (g_Game.IsServer())
m_Manager.KillPlayer();
break;
case EmoteConstants.EMOTE_SUICIDE_BLEED:
if (g_Game.IsServer())
m_Manager.CreateBleedingEffect(m_callbackID);
break;
case EmoteConstants.EMOTE_SUICIDE_SIMULATION_END:
m_player.DeathDropHandEntity();
m_player.StartDeath();
break;
}
}
}Constantes de eventos de animação customizados para emotes:
| Constante | Valor | Descrição |
|---|---|---|
EMOTE_SUICIDE_DEATH | 1 | Jogador morre (lado servidor) |
EMOTE_SUICIDE_BLEED | 2 | Efeito de sangramento começa |
EMOTE_SUICIDE_SIMULATION_END | 3 | Simulação termina, física assume |
UA_ANIM_EVENT | 11 | Evento genérico de animação de ação do usuário |
Iniciando Ações com Animações
Ações de corpo inteiro usam StartCommand_Action(), ações aditivas usam AddCommandModifier_Action():
// Ação de corpo inteiro (assume toda a animação do personagem)
HumanCommandActionCallback callback = player.StartCommand_Action(
DayZPlayerConstants.CMD_ACTIONFB_BANDAGE, // ID da animação
ActionBandageCB, // typename do callback
DayZPlayerConstants.STANCEMASK_CROUCH // posturas válidas
);
// Ação aditiva (sobreposta à locomoção)
HumanCommandActionCallback callback = player.AddCommandModifier_Action(
DayZPlayerConstants.CMD_ACTIONMOD_DRINK, // ID da animação
ActionDrinkCB // typename do callback
);HumanCommandScript -- Animações Totalmente Customizadas
HumanCommandScript (scripts/3_game/human.c) fornece controle completo em nível de script sobre a animação do personagem. É a abordagem mais poderosa, mas também a mais complexa:
class HumanCommandScript
{
// Ciclo de vida
void OnActivate();
void OnDeactivate();
proto native void SetFlagFinished(bool pFinished);
// Controle de direção
proto native void SetHeading(float yawAngle, float filterDt, float maxYawSpeed);
// Sobrescritas para relatório de estado
int GetCurrentStance(); // padrão: STANCEIDX_ERECT
int GetCurrentMovement(); // padrão: MOVEMENT_IDLE
// Fases de atualização de animação
void PreAnimUpdate(float pDt); // defina variáveis de animação aqui
void PrePhysUpdate(float pDt); // após animação, antes da física
bool PostPhysUpdate(float pDt); // após física, retorne false para encerrar
// Auxiliares de PreAnimUpdate
proto native void PreAnim_CallCommand(int pCommand, int pParamInt, float pParamFloat);
proto native void PreAnim_SetFloat(int pVar, float pFlt);
proto native void PreAnim_SetInt(int pVar, int pInt);
proto native void PreAnim_SetBool(int pVar, bool pBool);
// Auxiliares de PrePhysUpdate
proto native bool PrePhys_IsEvent(int pEvent);
proto native bool PrePhys_IsTag(int pTag);
proto native bool PrePhys_GetTranslation(out vector pOutTransl);
proto native bool PrePhys_GetRotation(out float pOutRot[4]);
proto native void PrePhys_SetTranslation(vector pInTransl);
proto native void PrePhys_SetRotation(float pInRot[4]);
// Auxiliares de PostPhysUpdate
proto native void PostPhys_GetPosition(out vector pOutTransl);
proto native void PostPhys_GetRotation(out float pOutRot[4]);
proto native void PostPhys_SetPosition(vector pInTransl);
proto native void PostPhys_SetRotation(float pInRot[4]);
proto native void PostPhys_LockRotation();
}Iniciar um comando scriptado:
// A partir do typename (o motor cria a instância)
HumanCommandScript cmd = player.StartCommand_ScriptInst(MyCustomCommand);
// A partir de uma instância (você a cria)
MyCustomCommand cmd = new MyCustomCommand(player);
player.StartCommand_Script(cmd);HumanAnimInterface
Para interagir com variáveis do grafo de animação a partir de HumanCommandScript, use HumanAnimInterface:
class HumanAnimInterface
{
proto native TAnimGraphCommand BindCommand(string pCommandName);
proto native TAnimGraphVariable BindVariableFloat(string pVariable);
proto native TAnimGraphVariable BindVariableInt(string pVariable);
proto native TAnimGraphVariable BindVariableBool(string pVariable);
proto native TAnimGraphTag BindTag(string pTagName);
proto native TAnimGraphEvent BindEvent(string pEventName);
}Acesso via player.GetAnimInterface().
Modificador de Comando: Aditivos
HumanCommandAdditives fornece sobreposições de animação ambiente que executam continuamente junto com o comando principal:
class HumanCommandAdditives
{
proto native void SetInjured(float pValue, bool pInterpolate); // 0..1
proto native void SetExhaustion(float pValue, bool pInterpolate); // 0..1
proto native void SetTalking(bool pValue);
proto native void StartModifier(int pType);
proto native void CancelModifier();
proto native bool IsModifierActive();
}Acesso via player.GetCommandModifier_Additives(). Estes estão sempre ativos e se misturam por cima de qualquer comando em execução.
Referência Rápida de DayZPlayerConstants
IDs de Animação de Ação Comuns
Aditivos (CMD_ACTIONMOD_*) -- reproduzidos em pé/agachado:
| Constante | ID | Descrição |
|---|---|---|
CMD_ACTIONMOD_DRINK | 0 | Beber |
CMD_ACTIONMOD_EAT | 1 | Comer |
CMD_ACTIONMOD_CRAFTING | 22 | Craftar |
CMD_ACTIONMOD_PICKUP_HANDS | 500 | Pegar com as mãos |
CMD_ACTIONMOD_ITEM_ON | 509 | Ligar item |
CMD_ACTIONMOD_ITEM_OFF | 510 | Desligar item |
CMD_ACTIONMOD_STARTENGINE | 300 | Ligar motor do veículo |
CMD_ACTIONMOD_SHIFTGEAR | 405 | Trocar marcha do veículo |
Corpo inteiro (CMD_ACTIONFB_*) -- reproduzidos deitado ou em posturas especiais:
| Constante | ID | Descrição |
|---|---|---|
CMD_ACTIONFB_DRINK | 0 | Beber (deitado) |
CMD_ACTIONFB_BANDAGE | 58 | Bandar |
CMD_ACTIONFB_CRAFTING | 59 | Craftar (agachado) |
CMD_ACTIONFB_DIG | 88 | Cavar |
CMD_ACTIONFB_ANIMALSKINNING | 66 | Esfolar animal |
CMD_ACTIONFB_STARTFIRE | 65 | Acender fogueira |
CMD_ACTIONFB_PICKUP_HEAVY | 519 | Pegar item pesado |
IDs de Comando de Gesto
Gestos aditivos (CMD_GESTUREMOD_*) -- usados em pé/agachado:
| Constante | ID |
|---|---|
CMD_GESTUREMOD_GREETING | 1000 |
CMD_GESTUREMOD_POINT | 1001 |
CMD_GESTUREMOD_THUMB | 1002 |
CMD_GESTUREMOD_SILENCE | 1004 |
CMD_GESTUREMOD_TAUNT | 1005 |
CMD_GESTUREMOD_HEART | 1007 |
CMD_GESTUREMOD_CLAP | 1101 |
CMD_GESTUREMOD_SURRENDER | 1112 |
Gestos de corpo inteiro (CMD_GESTUREFB_*) -- usados deitado ou em posturas exclusivas:
| Constante | ID | Postura |
|---|---|---|
CMD_GESTUREFB_SOS | 1053 | em pé |
CMD_GESTUREFB_SALUTE | 1050 | em pé |
CMD_GESTUREFB_CAMPFIRE | 1051 | agachado |
CMD_GESTUREFB_SITA | 1054 | agachado |
CMD_GESTUREFB_SITB | 1055 | agachado |
CMD_GESTUREFB_LYINGDOWN | 1052 | agachado |
CMD_GESTUREFB_DANCE | 1109 | em pé |
CMD_GESTUREFB_SURRENDERIN | 1113 | agachado/deitado |
Exemplos Práticos
Verificando Postura do Jogador
// Método 1: Via estado de movimento
HumanMovementState hms = new HumanMovementState();
player.GetMovementState(hms);
if (hms.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_CROUCH)
Print("Jogador está agachado");
if (hms.IsRaised())
Print("Arma está levantada");
// Método 2: Via máscara de postura (preferido para verificações multi-postura)
if (player.IsPlayerInStance(DayZPlayerConstants.STANCEMASK_PRONE | DayZPlayerConstants.STANCEMASK_RAISEDPRONE))
Print("Jogador está em alguma postura deitada");
// Método 3: Verificar comando atual
int cmdID = player.GetCurrentCommandID();
if (cmdID == DayZPlayerConstants.COMMANDID_SWIM)
Print("Jogador está nadando");Forçando Mudança de Postura
HumanCommandMove cmdMove = player.GetCommand_Move();
if (cmdMove)
{
// Forçar jogador a ficar em pé
cmdMove.ForceStance(DayZPlayerConstants.STANCEIDX_ERECT);
// Liberar a força (deixar jogador controlar a postura novamente)
// cmdMove.ForceStance(-1);
}Animando um Objeto Customizado (Porta / Alavanca)
// Na sua classe de item customizado
class MyLever extends ItemBase
{
protected bool m_IsActivated;
void ToggleLever()
{
m_IsActivated = !m_IsActivated;
if (m_IsActivated)
{
// Animar alavanca para posição "ligado" (fase 1)
SetAnimationPhase("lever_source", 1);
}
else
{
// Animar alavanca para posição "desligado" (fase 0)
SetAnimationPhase("lever_source", 0);
}
}
float GetLeverState()
{
return GetAnimationPhase("lever_source");
}
}O model.cfg correspondente definiria:
class AnimationSources
{
class lever_source
{
source = "user";
animPeriod = 0.3;
initPhase = 0;
};
};
class Animations
{
class lever_rotate
{
type = "rotation";
source = "lever_source";
selection = "lever";
axis = "lever_axis";
minValue = 0;
maxValue = 1;
angle0 = 0;
angle1 = -1.5708; // -90 graus em radianos
};
};Detectando Comando de Animação Ativo
// Verificar o que o jogador está fazendo atualmente
if (player.GetCommand_Vehicle())
{
HumanCommandVehicle vehCmd = player.GetCommand_Vehicle();
Print("No assento do veículo: " + vehCmd.GetVehicleSeat());
}
else if (player.GetCommand_Swim())
{
Print("Jogador está nadando");
}
else if (player.GetCommand_Fall())
{
HumanCommandFall fallCmd = player.GetCommand_Fall();
if (fallCmd.PhysicsLanded())
Print("Acabou de aterrissar");
}
else if (player.GetCommand_Action())
{
HumanCommandActionCallback actionCB = player.GetCommand_Action();
Print("Estado da ação: " + actionCB.GetStateString());
}Boas Práticas
Sempre verifique null nos getters de comando.
GetCommand_Move()retornanullquando o jogador não está no comando de movimento. Cada getter de comando pode retornar null.Use máscaras de postura, não índices de postura, para verificações multi-postura.
IsPlayerInStance()com flags de bitmask é mais limpo do que compararm_iStanceIdxcontra múltiplos valores.Prefira
SetAnimationPhase()ao invés deSetAnimationPhaseNow(). A versão interpolada fica mais suave. UseSetAnimationPhaseNow()apenas quando precisar de mudanças de estado instantâneas (carregando da persistência, inicialização).Mantenha os valores de fase de animação entre 0.0 e 1.0. O motor interpola entre
minValueemaxValuedefinidos nomodel.cfg. A fase é sempre normalizada.Não chame
StartCommand_*()de código arbitrário. Comandos devem ser iniciados a partir doCommandHandlerou em resposta a condições validadas. Iniciar comandos em momentos errados pode causar corrupção do estado de animação.Para emotes, use o EmoteManager.
StartCommand_Action()direto com IDs de gesto funciona em ferramentas de debug, mas ignora a sincronização, interrupção e lógica de gerenciamento de estado que oEmoteManagerfornece.
Observado em Mods Reais
- Mod Expansion usa
HumanCommandScriptpara animações customizadas de entrada/saída de veículos e sincronização de posição de membros de grupo. - COT (Community Online Tools) usa
StartCommand_Action()diretamente em suas ferramentas de câmera/cinemáticas para forçar animações de gestos em jogadores para capturas de tela. - Mods de construção de base (BuilderItems, BuildAnywhere) fazem uso intenso de
SetAnimationPhase()para mostrar/ocultar estágios de construção em estruturas customizadas. - Barril vanilla alterna
SetAnimationPhase("Lid", 0/1)para abrir/fechar, demonstrando o padrão standard para objetos de estado binário.
Teoria vs Prática
Teoria: HumanCommandScript permite criar comandos de animação totalmente customizados com controle completo de física. Prática: A maioria dos modders nunca precisa dele. O sistema de ações (StartCommand_Action / AddCommandModifier_Action) com IDs de animação pré-existentes cobre 95% dos casos de uso. HumanCommandScript é reservado para casos extremos como tipos de veículos customizados ou modos de movimento inteiramente novos.
Teoria: Você pode registrar eventos de animação customizados e recebê-los em callbacks. Prática: Eventos de animação customizados requerem arquivos de animação customizados (.rtm / mudanças no grafo de animação), que precisam do Workbench e da cadeia de ferramentas de animação do DayZ. A maioria dos modders reutiliza IDs de animação existentes e conta com callbacks de mudança de estado (OnStateChange, OnFinish) ao invés de eventos customizados.
Teoria: Qualquer emote pode ser reproduzido em qualquer postura. Prática: O sistema de máscara de postura é estrito. Se sua classe de emote declara m_StanceMaskFullbody = STANCEMASK_ERECT mas o jogador está agachado, o emote não será reproduzido. O método DetermineEmoteData() verifica aditivo primeiro, depois corpo inteiro, e falha silenciosamente se nenhum corresponder.
Erros Comuns
Verificar o comando errado. Chamar
GetCommand_Move()enquanto o jogador está em um veículo retornanull. Sempre verifiqueGetCurrentCommandID()primeiro ou faça verificação de null no resultado.Esquecer que comandos de ação substituem o movimento.
StartCommand_Action()é de corpo inteiro -- o jogador para de se mover. Se você quer uma animação de sobreposição, useAddCommandModifier_Action()em vez disso.Usar
SetAnimationPhase()com nomes de fonte errados. A string deve corresponder exatamente ao nome da classeAnimationSourcesnomodel.cfg. Não há erro se o nome estiver errado -- nada acontece.Confundir índice de postura e máscara de postura.
ForceStance()recebe um valorSTANCEIDX_*(inteiro 0-5).StartCommand_Action()eIsPlayerInStance()recebem valoresSTANCEMASK_*(flags de bitmask). Passar um onde o outro é esperado produz comportamento errado silencioso.Não verificar
EmoteCondition(). Tentar reproduzir um emote que requer mãos vazias enquanto segura um item vai falhar. O sistema de emotes verifica condições antes da reprodução, mas não registra um aviso.Chamar
StartCommand_*()na máquina errada. A maioria dos comandos só deve ser iniciada na máquina que possui o jogador (servidor para IA, cliente para jogador controlado). Iniciar comandos no lado errado causa dessincronização.
Compatibilidade e Impacto
- O sistema de comandos de animação é de nível de motor e permaneceu estável entre as versões do DayZ.
HumanCommandMove,HumanCommandActionCallbackeDayZPlayerConstantsnão mudaram sua API em anos. EmoteManagereEmoteBasesão modificáveis viamodded class. Você pode adicionar novos emotes estendendoEmoteBasee modificandoEmoteConstructor.- Animações de objetos via
SetAnimationPhase()são o padrão usado por todos os itens vanilla e pela maioria dos mods. A API é estável e bem testada. HumanCommandScripté uma API para usuários avançados. Funciona, mas é pouco documentada pela Bohemia. O melhor recurso de aprendizado é ler implementações vanilla emdayzplayerimplement.c.- Adicionar novos IDs de animação (valores CMD_ACTIONFB/MOD) requer entradas correspondentes no grafo de animação do jogo, o que só é possível com acesso ao Workbench e ferramentas de edição de modelos p3d.
Arquivos fonte referenciados neste capítulo:
| Arquivo | Conteúdo |
|---|---|
scripts/3_game/human.c | HumanInputController, HumanCommandActionCallback, HumanCommandMove, HumanCommandFall, HumanCommandClimb, HumanCommandSwim, HumanCommandVehicle, HumanCommandScript, HumanMovementState, HumanAnimInterface, classe Human |
scripts/3_game/dayzplayer.c | DayZPlayerConstants (posturas, comandos, IDs de ação/gesto) |
scripts/3_game/constants.c | EmoteConstants (IDs de emote, IDs de evento de suicídio) |
scripts/3_game/entities/entity.c | SetAnimationPhase, GetAnimationPhase |
scripts/4_world/classes/emotemanager.c | EmoteCB, EmoteLauncher, EmoteManager |
scripts/4_world/classes/emoteclasses/emotebase.c | Classe EmoteBase |
scripts/4_world/classes/emoteclasses/emoteclasses.c | Todas as subclasses vanilla de EmoteBase |
scripts/4_world/classes/emoteconstructor.c | EmoteConstructor (registro de emotes) |
