Como amarrar um lançamento de bala com uma animação de disparo

15

Digamos que você tenha uma animação que deseja que aconteça ao disparar uma bala. Como você faria com que o marcador aparecesse no final da animação. A única coisa que consigo descobrir é saber o tempo de duração da animação e adiar a configuração da posição dos marcadores e ativá-la até que essa quantidade de tempo tenha passado. Eu só estava me perguntando se esta é a melhor abordagem, como todos os outros lidam com isso?

Edição: Eu acho que estou tendo problemas para redigir a pergunta corretamente.

Minha pergunta

tp0w3rn
fonte
Obrigado! Muitas boas idéias de todos vocês, gosto da ideia de usar um retorno de chamada. Acho que vou tentar implementar isso, realmente não queria confiar no controle de tempo.
tp0w3rn

Respostas:

8

Fundamentalmente, você está no caminho certo - você precisa saber quanto tempo uma animação dura para fazer esse tipo de coisa. As animações são mais do que apenas uma coleção de quadros; existem todos os tipos de informações que você precisa. Por exemplo, quantos quadros existem, a animação é reproduzida, com que rapidez é reproduzida (por exemplo, 10 quadros de animação por segundo ou 25 ou 60?). Toda animação pode ser definida em termos de alguns dados, os quais algum código de animação generalizado pode visualizar e reproduzir. Você deve encapsular a parte da animação em seu próprio pedaço de código, que não reconhece nada, exceto essas definições de animação e como exibir os quadros de imagem individuais. Ou seja, tenha um objeto de animação que você possa carregar, iniciar a reprodução, interromper a reprodução e dizer para renderizar em um local específico na tela.

Uma abordagem flexível é usar um tipo de definição de animação para encapsular esse tipo de informação. Então, em vez de apenas dizer "a animação X é todos esses quadros, basta reproduzi-los", você obtém algo um pouco mais complexo.

Por exemplo, com algum tipo de formato de dados simulado

animações =
{
  {name = "walk", arquivos = "walk * .png", frameCount = "12", loop = "true"},
  {name = "fire" arquivos = "fire * .png" frameCount = "6",
       events = {
           {name = "bulletLeavesGun", frame = "4", param1 = "43", param2 = "30"}
       }
  }
}

Portanto, seu código diz algo como:

currentAnimation = animations.Get("fire");
currentAnimation.Play();

Como você detecta eventos pode ser com o código de animação retornando a ligação (ou seja, quando ele detecta um novo evento porque a animação foi reproduzida em um determinado quadro, ele chama o código do jogo para informar sobre o novo evento) ou pesquisando o animação assim:

List<Event> events = currentAnimation.EventsSinceLastCheck();
foreach (AnimationEvent event in events)
{
    if (event.name == "bulletLeavesGun")
    {
        Vector2 bulletPosition = new Vector2(event.param1, event.param2);
        Vector2 actualBulletPosition = new Vector2(
                 character.x + bulletPosition.x, 
                 character.y + bulletPosition.y);
        CreateBulletAt(actualBulletPosition);
    }
}

Pontos a serem observados:

  • O código de animação deve existir separadamente do código do jogo. Você realmente não quer que seu código de jogo esteja muito ligado aos detalhes da reprodução de animação.
  • O código de animação sabe se deve ou não fazer loop com base na definição de animação
  • O código da animação sabe quando a animação é concluída e pode retornar a outro código para dizer 'ei, a animação chamada "fogo" acabou de terminar, o que você deseja fazer agora?
  • O código de animação não sabe nada sobre eventos, exceto que eles têm um nome e alguns dados arbitrários associados a eles (param1 e param2)
  • O código de animação sabe em qual quadro ele está atualmente e, quando muda para um novo quadro, pode verificar e dizer 'oh, estou no quadro 4 agora, isso significa que esse evento chamado "fogo" acabou de acontecer, adicione isso a minha lista de eventos recentes, para que eu possa contar a quem perguntar '.

Se você não precisar que o disparo de bala aconteça na animação, mas apenas quando terminar, você poderá se safar de um sistema muito menos complexo sem a noção de eventos. Mas você ainda desejará um sistema em que as animações sejam reproduzidas por conta própria, saiba quanto tempo elas duram e poderá retornar ao código do jogo quando uma animação for concluída.

MrCranky
fonte
Não concordo em manter animações com lógica (no quadro 4 agora, isso significa que esse evento chamado "fogo" acabou de acontecer). As animações devem ser cegas e burras. Eu tive que fazer algumas lógicas do lado do servidor e rasgar animações e a interface do usuário de um jogo é algo que eu não quero fazer novamente. Eu realmente recomendaria o uso de animações muito curtas e segmentadas, reproduzi-las em paralelo para que as lógicas permitissem que as lógicas disparassem sequências de animação na velocidade definida pela lógica. Nunca faça testes de lógica para o status de uma animação.
Coyote
Dividir a animação em pedaços parece bastante desnecessário. Eu concordo em não pesquisar o status da animação, mas isso ainda deixa sua primeira recomendação. Não sei se ele quis dizer um sistema de eventos separado para separar o código de animação do resto do jogo (padrão de observador?), Mas é assim que eu faria. Nem a "lógica", como você coloca, deve saber sobre o código de animação ou vice-versa.
Jhocking 04/10
@ Coiote Eu diria que você está misturando duas coisas separadas. Sim, a lógica do lado do servidor sempre deve ser independente do visual (porque você não deseja executar o sistema de animação apenas para descobrir quando um marcador é disparado), mas isso não ajudará a criar um sistema de animação no cliente . No cliente, você absolutamente não quer que o visual seja escravo do servidor, porque isso seria horrível - marcadores aparecendo em momentos estranhos e fora de sincronia com o personagem, porque houve um pico de atraso entre o jogo e o servidor . Não há nenhuma razão que você não pode ter ambos (cont ...)
MrCranky
@Coyote (cont ...), a jogabilidade pode ser conduzida pelo servidor dissociado do visual. Portanto, o marcador é disparado no momento X no servidor, e o cliente reflete essa ação começando a reproduzir a animação de disparo imediatamente, com o visual do disparo de bala ficando alguns quadros atrás da simulação de jogabilidade. Todos os tipos de compromissos precisam ser feitos entre fidelidade visual e simulação de jogo, por isso dizer que "as animações devem ser cegas e burras" é simplesmente ingênuo. Às vezes, os eventos precisam absolutamente ser vinculados aos quadros de animação, porque nenhum outro método fará com que pareçam ou soem corretos.
MrCranky
@Coyote Atualmente, penso nisso, o disparo de balas é um exemplo terrível disso, principalmente por causa da resposta do thedaian abaixo. O disparo deve acontecer imediatamente. Um exemplo melhor seria o disparo de um VFX em pó quando um personagem cair - o servidor e o cliente sincronizarão quando o personagem começar a pular, mas a exibição visual é deixada para o cliente. E quando a animação atinge o quadro certo onde o pé atinge o chão, o evento VFX deve disparar. Da mesma forma, os eventos são necessários se for necessário tomar uma decisão em um determinado quadro de animação para ramificar para outra animação.
MrCranky
3

De alguma forma, você terá que esperar até que a animação seja concluída e criar o marcador nesse ponto.

Apenas definir um cronômetro funcionará, se você tiver certeza de que sua taxa de animação é fixa. Como uma variação menor, você pode ter um código interno ao marcador que o faz esperar invisivelmente por um momento antes de aparecer e se mover.

Dependendo da sua plataforma de desenvolvimento, você pode ter algum tipo de função de atualização de animação ou retorno de chamada que permita responder no momento exato em que a animação atingir o ponto desejado. É assim que eu faria com o Flixel, por exemplo.

Gregory Avery-Weir
fonte
11
O addAnimationCallbackmétodo de Flixel pode ser usado na entidade de tiro. Na função de retorno de chamada, é possível ver se o quadro atual da animação de disparo é o quadro que deve criar uma entidade de marcador. Se for, então você pode adicionar um marcador na tela.
Snow Blind
2

Resposta direta: supondo que você tenha uma animação que deseja reproduzir quando o jogador pressionar o botão 'disparar' e, em seguida, solte uma bala depois que terminar de jogar. Idealmente, você deve evitar codificar o tempo da animação e acionar o marcador quando a animação terminar (usando uma função de retorno de chamada ou algo assim, dependendo da sua plataforma). Não consigo pensar em nenhum outro método para fazer isso que não seja excessivamente complexo.

Resposta alternativa ao design do jogo: A menos que haja uma razão realmente boa para evitar isso, eu evitaria atrasar o pressionamento do botão 'disparar' e o marcador aparecer. A menos que a animação seja muito, muito curta (um ou dois quadros, no máximo, basicamente um flash de focinho), isso fará com que a resposta do botão de disparo pareça lenta, e será irritante para um jogador comum. Mesmo que você decida usar uma animação antes que as balas sejam lançadas (RPGs baseados em turnos e jogos táticos seriam razões aceitáveis ​​para fazer isso), eu pensaria em incluir uma opção "desativar animações" em algum lugar, para permitir que o jogo para se mover mais rápido, se o jogador quiser.

thedaian
fonte
Sim, não faça o fogo reagir lentamente. Este é exatamente o problema comum do salto; os animadores fazem uma grande conclusão, mas os jogadores esperam estar no ar assim que apertam o botão.
Jhocking 4/10
2

Como MrCranky diz; mantenha animação e lógica separadas.

Mas o mais importante, as lógicas devem permanecer a parte principal .

  • Quando o botão de disparo é pressionado, você deve acionar a " ação " do empate no estado do seu personagem (lógica).
  • Esta ação deve acionar a animação do desenho com todos os parâmetros (tempo de vida, etc.).
  • Quando a ação de empate estiver concluída, você poderá acionar a ação de fogo (pode disparar uma ou mais vezes, dependendo da arma)
  • Esta ação pode gerar marcadores e acionar a animação de fogo .

Lembre-se de que controlar a interface do usuário a partir das lógicas é a única maneira de garantir que você poderá manter, reutilizar e compartilhar suas lógicas mais tarde (novo renderizador, novo jogo, versão do servidor sem renderizador ...)

Coiote
fonte
1

Primeiro, eu usaria um sistema de eventos (padrão de observador?) Para separar partes do código. Isso não é apenas para a animação, mas certamente se aplica a ela. Em seguida, o código de animação pode simplesmente dizer dispatchEvent (event) e alguma outra parte do código escuta esse evento, sem que nenhuma parte do código precise se conhecer.

Agora, o código da animação precisa realmente ter uma referência ao distribuidor de eventos (feito facilmente com injeção de dependência) e você precisa de algum XML ou JSON que realmente defina suas animações. Algo como:

{
  animation: {
    name: shoot,
    length: 12,
    spritesheet: shoot.png
    event: {
      frame: 4,
      name: bulletLeavesGun,
    },
  },
}

Leia os dados ao carregar a animação e faça com que o código da animação despache o evento quando estiver nesse quadro durante a reprodução.

jhocking
fonte