O Java AWT é adequado para a renderização de jogos em 2D?

8

[Reposicionando essa pergunta do stackoverflow, como foi apontado que ele se encaixa melhor aqui.]

Atualmente, estou portando meu mecanismo de jogo 2D para Java. Eu olhei para algumas das bibliotecas de jogos apontadas aqui no stackoverflow. No entanto, as que eu olhei eram bastante simplistas e nem declararam se suportavam coisas como transparência alfa, então decidi portar meu renderizador C ++ para o qual eu já havia escrito a lógica.

Este renderizador é um renderizador de software puro que usa o ladrilho para evitar a renderização desnecessária. Otimizei o desempenho de rolagem criando um "buffer fora da tela" um pouco maior que o meu painel de saída e colocando esse buffer fora da tela na minha saída em todos os quadros. Dessa forma, eu poderia evitar redesenhar os blocos desnecessariamente, apenas porque eu rolei um pixel no mapa.

Eu usei o AWT do Java para implementá-lo, usando um BufferedImage grande para o buffer fora da tela. O uso da CPU é bom (cerca de duas vezes o que eu tinha em C ++), mas há um problema estranho com a rolagem contínua, onde a cada segundo, o renderizador fica atrasado por cerca de 0,2 segundos.

Como não há nada no meu próprio código que ocorra nesses períodos, e como os picos desaparecem se eu não puxar meu buffer para fora da tela na tela principal, só posso concluir que o Java está fazendo uma otimização interna própria. No entanto, não tenho certeza do que faz, nem sei quais das minhas otimizações eu precisaria remover para me livrar dos picos. Além disso, pode ser que o java AWT não tenha sido feito com rolagem contínua e alta de FPS em mente, e isso é totalmente inutilizável para essa finalidade.

Existe alguma maneira de me livrar desses picos?

cib
fonte
4
Poderia ser o coletor de lixo batendo em você?
bummzack
@bummzack: Possivelmente. No criador de perfil, fica assim: i.imgur.com/EMxkA.png No entanto, não tenho certeza de como reduziria esse efeito, principalmente se ele for causado por minhas chamadas para graphics.drawImage
cib

Respostas:

4

Embora eu não tenha certeza sem olhar para o seu código, parece que seu problema é o coletor de lixo. Em Java, você tem coletas de lixo maiores e menores de vez em quando. O menor usa um pouco do seu processador, mas não o incomoda muito. As principais coleções podem ser um problema real para aplicativos em tempo real, como jogos, pois eles realmente pausam tudo enquanto estão em execução.

Existem duas opções para resolver isso. Primeiro, você pode ajustar a JVM para garantir que menos coleções importantes ocorram. Em segundo lugar (e recomendado), você pode garantir que não deixa muito lixo. Basta verificar onde, no seu aplicativo, você cria muitos objetos (nos meus jogos, essas são geralmente as classes vector3) e certifique-se de reutilizá-las o máximo possível (especialmente em loops internos, etc.).

Pjotterke
fonte
2

Sim.

Para jogos 2D baseados em sprites, o AWT pode ser usado para lidar com a renderização com grande efeito. Pode até ser acelerado por hardware , dependendo do hardware disponível.

Sem nenhum código ou snippets de perfil detalhados, é difícil dizer qual é o problema. O melhor que posso fazer é oferecer algumas dicas básicas para trabalhar com Java e AWT ao criar jogos.

Trabalhando com o coletor de lixo

O GC em Java é algo que precisamos ter em mente ao criar nossos jogos. Ele será executado periodicamente e procurará objetos que não tenham nenhuma referência a eles e os removerá da memória. Esse processo de remoção é lento e provavelmente é a causa do engate que você está enfrentando.

Minha sugestão é evitar a criação de referências a objetos que não serão mantidas por toda a vida útil da execução (ou pelo menos o máximo possível). O objetivo ideal é garantir que o GC não tenha nada para fazer sempre que for executado.

Na prática, você pode acabar com muitas variáveis ​​estáticas que você reutiliza ao longo do jogo. Aqui está um exemplo muito bem elaborado de como eu tento lidar com isso:

public final class Resources {
    public static Map<int, String> strings;
    public static Map<int, Texture> textures;
    public static Map<int, GameObject> objects;
    public static Map<int, SoundEffect> sounds;
}

Durante o carregamento das telas, é onde você pode aumentar ou diminuir suas Mapinstâncias usando a newpalavra - chave. Mas durante o jogo, você deve evitar isso o máximo que puder. Se algo for destruído durante o jogo, defina uma bandeira no objeto para que você saiba que não é algo ativo no momento. Se você precisar gerar um novo objeto, vá Mapaté você até encontrar um que não esteja ativo, defina suas propriedades e marque-o como ativo.

Isso é algo que você deve ter em mente ao usar Java para aplicativos sensíveis ao desempenho, independentemente de estar usando AWT, JavaFX ou OpenGL para fazer a renderização.

Tela de pintura

Para o AWT em particular, eu recomendo usar a classe Canvas para renderizar tudo por vários motivos:

  • Você tem um controle melhor de quando as coisas são renderizadas. Isso significa que você pode escrever seu próprio ciclo de jogo e fazer coisas como interpolação, extrapolação, limitação de taxa etc.
  • Parece ter um desempenho melhor. Consegui colocar mais coisas na tela ao mesmo tempo, com uma taxa de quadros aceitável, em vez de tentar animar um monte de objetos Label e Image.
  • É mais fácil incorporar nos editores. Ser capaz de configurar um quadro com controles GUI normais e apontar a lógica de renderização do jogo para um Canvas significava que eu poderia reutilizar o código de renderização do jogo nas ferramentas do editor.
  • Permite fácil acesso à API Java2D (também conhecida como classe Graphics2D ).
Cifra
fonte