🏗️ Cap 12: Arquitetura de Jogo (MVVM)
🎯 Objetivo da Aula: Ao final desta aula, você entenderá o padrão MVVM (Model-View-ViewModel). Você aprenderá a separar o “Cérebro” do seu jogo (a lógica de pontos e vida) do “Corpo” (a interface visual), usando as pastas padrão do curso.
🏢 O Cenário Prático (Seu Desafio): Até agora, você colocou toda a lógica (contas, variáveis) dentro da mesma função da tela. Isso funciona para apps pequenos, mas em um jogo grande vira uma bagunça impossível de consertar! Seu desafio é criar o Motor do Jogo (ViewModel): um local separado onde a vida do personagem é calculada, deixando a tela apenas com a função de mostrar o desenho.
🧠 Fundamentos: A Teoria Traduzida
📖 Dicionário do Programador
- View (Visão): É a tela (o Compose). Ela apenas desenha o que o Garçom traz.
- ViewModel (O Garçom): É quem manda no estado da tela. Ele faz os cálculos e avisa a tela: “Ei, mude o texto para 50 pontos!”.
- Encapsulamento: É esconder os dados sensíveis. A tela não pode mudar a vida do jogador diretamente; ela deve pedir ao Garçom para fazer isso.
🎨 O Mapa das Pastas (Revisão)
Crie estas pastas no seu projeto br.com.curso.pokedex (o mesmo AppPokedexMestre_SeuNome desde o Cap 07):
ui/screens/-> Coloque suas funções@Composableaqui.ui/viewmodel/-> Coloque suas classesViewModelaqui.
graph TD
A["View: Tela do Game"] <-->|Observa o Estado| B["ViewModel: Garçom da Lógica"]
B <-->|Gerencia| C["Model: Dados do Personagem"]
📦 Dependência Gradle
Para usar viewModel() dentro de uma função @Composable, adicione no build.gradle (Module: app), dentro de dependencies { }:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0'
📦 Imports Necessários
Antes de copiar os códigos abaixo, adicione estes imports no topo do seu arquivo .kt:
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
📖 Exemplo Passo a Passo: O Garçom de Pontos
- Crie o arquivo
JogoViewModel.ktna pastaui/viewmodel. - Use o prefixo
_para o estado privado (ninguém de fora mexe) e uma variável pública para a tela ler.
package br.com.curso.pokedex.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.State
class JogoViewModel : ViewModel() {
// 1. O dado secreto (Só o Garçom mexe)
private val _pontos = mutableStateOf(0)
// 2. O dado que a tela pode ver
val pontos: State<Int> = _pontos
// 3. A ação que muda o dado
fun ganharPonto() {
_pontos.value += 10
}
}
🛠️ Prática Obrigatória 1:
Crie uma classe PersonagemViewModel. Ela deve ter um estado privado para vida (começando em 100) e uma função receberDano(). A função deve tirar 10 de vida, mas nunca deixar ficar menor que 0.
🛠️ Prática Obrigatória 2:
Crie a tela @Composable chamada ArenaScreen na pasta ui/screens. Ela deve usar o seu PersonagemViewModel para mostrar a vida atual e ter um botão “Receber Golpe”.
🔑 Gabarito Passo a Passo:
ViewModel:
class PersonagemViewModel : ViewModel() {
private var _vida = mutableStateOf(100)
val vida: State<Int> = _vida
fun receberDano() {
if (_vida.value > 0) _vida.value -= 10
}
}
Screen:
@Composable
fun ArenaScreen(vm: PersonagemViewModel = viewModel()) {
Column {
Text("Vida: ${vm.vida.value}")
Button(onClick = { vm.receberDano() }) { Text("Receber Golpe") }
}
}
📤 Instruções de Entrega (Microsoft Teams):
- Envie o código do seu
ViewModele da suaScreen. - Submeta no canal de tarefas.