Problemas no Android threads envolvendo minha cabeça em torno do design

9

Estou tendo problemas para entender o design de jogos. Na plataforma Android, tenho uma atividade e defino sua visualização de conteúdo com uma visualização de superfície personalizada. A vista de superfície personalizada atua como meu painel e eu crio instâncias de todas as classes e faço todo o desenho e cálculo lá.

Pergunta: Em vez disso, devo criar as instâncias de outras classes na minha atividade?

Agora, crio uma classe de thread personalizada que lida com o loop do jogo.

Pergunta: Como uso essa classe em todas as minhas atividades? Ou tenho que criar uma instância separada da classe de thread estendida a cada vez?

No meu jogo anterior, eu tinha vários níveis que precisavam criar uma instância da classe thread e na classe thread tive que definir métodos construtores para cada nível separado e, no loop, use uma instrução switch para verificar qual nível ele precisa renderizar e atualização. Desculpe se isso parece confuso.

Eu só quero saber se o método que estou usando é ineficiente (o que provavelmente é) e como proceder para projetá-lo da maneira correta. Eu li muitos tutoriais por aí e ainda estou tendo muitos problemas com esse tópico em particular. Talvez um link para alguns tutoriais que explicam isso? Obrigado.

semajhan
fonte

Respostas:

13

Eu recomendo que você tem um render thread (usando Canvas/ OpenGL ES, Canvasé provavelmente um pouco mais fácil de configurar) e um fio de jogo onde você colocar o seu lógica do jogo.

Para realmente "carregar" o jogo, você pode criar uma classe GameEngine e tornar esse o ponto central do seu aplicativo. Quando o renderizador estiver pronto, você poderá criar um retorno de chamada para a instância GameEngine que criará e iniciará dois threads usando um Runnablepara a renderização e outro Runnablepara a lógica do jogo.

Código de amostra:

Início da aplicação

private GameEngine engine;
private CanvasRenderer renderer;

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   // Create instances of your two Runnable classes and pass that into
   // the GameEngine constructor.
   // Create an instance of the game engine.
   engine = new GameEngine(canvasRunnable, gamelogicRunnable);
   renderer = new CanvasRenderer(this, engine); 
   setContentView(renderer); 
}

CanvasRenderer

private GameEngine engine;    

// Save your instance from the GameEngine reference in your constrcutor and make
// a global initializion for your GameEngine instance.  

@Override
public void surfaceCreated(SurfaceHolder holder) {  
   // One time setup here.
   // When your view is ready, make this callback to the 
   // GameEngine.
   engine.surfaceIsReady();
}

Motor do jogo

private Thread canvasThread;
private CanvasRunnable canvasRunnable;
// You should be able to figure out how to create a second thread
// where you should put your game logic. :)

// Constructor stuff like creating instances of your threads
// and passing references as you wish to those.
// Don't start the threads here.
// Remember to set references from your Runnable's into your Thread's 
// instances here!

/**
 * Callback. Now your renderer is ready and you
 * can start your threads.
 */
public void surfaceIsReady() {
   thread.setName("Canvas");
   thread.start();
   // Same for game logic.
}
Wroclai
fonte
Uau, obrigada. Eu gostei de como você explicou. Essa explicação esclarece todo o conceito para mim.
semajhan
@semajhan: Basta perguntar se você tem mais problemas. :)
Isso é o que tenho em minha mente: classe GameEngine que atua como um "link" ou "referência" a todas as outras classes do painel. Atividade> Painel> GameEngine> todas as outras classes.
semajhan
@semajhan: Exatamente. Apenas para seu conhecimento: se você decidir ir com OpenGL ESvocê, deve saber que o renderizador OpenGL ESjá possui seu próprio encadeamento e, nesse caso, não precisa criar e iniciar manualmente um novo Threade Runnablepara esse sistema.
Ignore este comentário.
semajhan
3

Normalmente, seu ciclo de jogo é independente em uma única atividade.

ao alternar Activity, você pausa / mata seu loop de jogo. As atividades separadas devem corresponder à pausa do jogo de qualquer maneira (por exemplo, porque você mudou para uma atividade "enviar email para amigos" ou "menu principal")

Para níveis extras, você não deve criar ou destruir novos threads ... a menos que tenha mudado para um "nível completo, carregando o próximo nível, aguarde" Atividade, e precisará reiniciar o "jogo principal" "Atividade de qualquer maneira. Mas mesmo nesse caso, você não está realmente criando threads "extras", está apenas criando um thread nessa atividade e matando / reiniciando / matando / reiniciando sequencialmente ... etc essa atividade. cada vez que um nível é concluído.


fonte
3

Se você entende alemão, este tutorial é muito bom.

Para o idioma inglês, eu posso recomendar este tutorial

Em relação à classe de encadeamento: não sei se é realmente necessário que você possa fazer referência a todas as classes em seu aplicativo. No meu jogo, resolvi assim:

A classe responsável por desenhar a GUI principal possui um método de renderização substituído. Nesse método, é chamada uma classe de encadeamento que atualiza todos os elementos da GUI e processa as entradas do usuário.

O thread também é responsável por manter uma taxa de quadros constante. Dependendo do jogo que você está desenvolvendo, isso pode ser importante.

RoflcoptrException
fonte
Esse tutorial de alemão é legal, mas foi traduzido aproximadamente para o google, então é meio difícil de entender.
precisa saber é