# Padrões de Design - SevenX Engine
Este documento apresenta padrões comuns e soluções para problemas frequentes ao desenvolver jogos com a SevenX Engine.
## Padrões de Gameplay
### 1. Sistema de Vidas do Jogador
```rust
struct MyGame {
player_health: i32,
max_health: i32,
}
impl GameState for MyGame {
fn new() -> Self {
MyGame {
player_health: 100,
max_health: 100,
}
}
fn update(&mut self, dt: f32, input: &InputHandler, world: &mut World) {
// Verifica colisões com inimigos
if let Some(player) = world.game_objects.iter()
.find(|obj| obj.object_type == ObjectType::Player)
{
if let Some(player_rect) = player.get_collision_rect() {
for enemy in world.game_objects.iter()
.filter(|obj| obj.object_type == ObjectType::Enemy)
{
if let Some(enemy_rect) = enemy.get_collision_rect() {
if player_rect.intersects(&enemy_rect) {
self.player_health -= 10;
println!("Vida: {}/{}", self.player_health, self.max_health);
}
}
}
}
}
// Game Over
if self.player_health <= 0 {
println!("Game Over!");
// Reset ou sair
}
}
fn draw(&mut self, _world: &World, _pixels: &mut [u8]) {}
}
```
### 2. Sistema de Pontuação
```rust
struct MyGame {
score: u32,
high_score: u32,
}
impl MyGame {
fn collect_coin(&mut self, points: u32) {
self.score += points;
if self.score > self.high_score {
self.high_score = self.score;
}
println!("Score: {} | High Score: {}", self.score, self.high_score);
}
}
```
### 3. Spawn de Inimigos por Ondas
```rust
struct MyGame {
wave: u32,
enemies_spawned: u32,
spawn_timer: f32,
spawn_interval: f32,
}
impl GameState for MyGame {
fn new() -> Self {
MyGame {
wave: 1,
enemies_spawned: 0,
spawn_timer: 0.0,
spawn_interval: 2.0,
}
}
fn update(&mut self, dt: f32, input: &InputHandler, world: &mut World) {
let enemies_per_wave = 5 + (self.wave * 2);
// Spawn inimigos
if self.enemies_spawned < enemies_per_wave {
self.spawn_timer += dt;
if self.spawn_timer >= self.spawn_interval {
self.spawn_timer = 0.0;
self.spawn_enemy(world);
self.enemies_spawned += 1;
}
}
// Verifica se todos inimigos foram derrotados
let enemy_count = world.game_objects.iter()
.filter(|obj| obj.object_type == ObjectType::Enemy && obj.active)
.count();
if enemy_count == 0 && self.enemies_spawned >= enemies_per_wave {
self.next_wave();
}
}
fn draw(&mut self, _world: &World, _pixels: &mut [u8]) {}
}
impl MyGame {
fn spawn_enemy(&self, world: &mut World) {
use rand::Rng;
let mut rng = rand::thread_rng();
let x = rng.gen_range(0.0..800.0);
let enemy = GameObject::new(x, 0.0, ObjectType::Enemy)
.with_physics(Physics::new().with_velocity(0.0, 100.0))
.with_collider(Collider::new(32.0, 32.0))
.with_z_index(5);
world.add_object(enemy);
}
fn next_wave(&mut self) {
self.wave += 1;
self.enemies_spawned = 0;
self.spawn_interval *= 0.9; // Aumenta dificuldade
println!("Wave {}!", self.wave);
}
}
```
## Padrões de Movimento
### 1. Movimento com Aceleração
```rust
fn update_player_movement(&mut self, input: &InputHandler, player: &mut GameObject, dt: f32) {
let acceleration = 500.0;
let max_speed = 200.0;
if let Some(physics) = &mut player.physics {
if input.is_key_pressed(KeyCode::ArrowRight) {
physics.velocity.0 = (physics.velocity.0 + acceleration * dt).min(max_speed);
player.facing_direction = Direction::Right;
} else if input.is_key_pressed(KeyCode::ArrowLeft) {
physics.velocity.0 = (physics.velocity.0 - acceleration * dt).max(-max_speed);
player.facing_direction = Direction::Left;
} else {
// Desacelera quando não há input
physics.velocity.0 *= 0.9;
}
}
}
```
### 2. Movimento em 8 Direções
```rust
fn update_8_direction_movement(&mut self, input: &InputHandler, player: &mut GameObject, dt: f32) {
let speed = 200.0;
let mut dx = 0.0;
let mut dy = 0.0;
if input.is_key_pressed(KeyCode::ArrowUp) { dy -= 1.0; }
if input.is_key_pressed(KeyCode::ArrowDown) { dy += 1.0; }
if input.is_key_pressed(KeyCode::ArrowLeft) { dx -= 1.0; }
if input.is_key_pressed(KeyCode::ArrowRight) { dx += 1.0; }
// Normaliza vetor diagonal
if dx != 0.0 && dy != 0.0 {
let length = (dx * dx + dy * dy).sqrt();
dx /= length;
dy /= length;
}
if dx != 0.0 || dy != 0.0 {
player.transform.translate(dx * speed * dt, dy * speed * dt);
player.current_action = PlayerAction::Walk;
} else {
player.current_action = PlayerAction::Idle;
}
}
```
### 3. Patrulha de Inimigo
```rust
struct Enemy {
patrol_start: f32,
patrol_end: f32,
direction: f32,
}
impl Enemy {
fn update_patrol(&mut self, enemy_obj: &mut GameObject, dt: f32) {
let speed = 50.0;
enemy_obj.transform.position.0 += self.direction * speed * dt;
// Inverte direção nos limites
if enemy_obj.transform.position.0 >= self.patrol_end {
self.direction = -1.0;
enemy_obj.facing_direction = Direction::Left;
} else if enemy_obj.transform.position.0 <= self.patrol_start {
self.direction = 1.0;
enemy_obj.facing_direction = Direction::Right;
}
}
}
```
## Padrões de Estado
### 1. Máquina de Estados do Jogo
```rust
#[derive(PartialEq)]
enum GameMode {
Menu,
Playing,
Paused,
GameOver,
}
struct MyGame {
mode: GameMode,
score: u32,
}
impl GameState for MyGame {
fn new() -> Self {
MyGame {
mode: GameMode::Menu,
score: 0,
}
}
fn update(&mut self, dt: f32, input: &InputHandler, world: &mut World) {
match self.mode {
GameMode::Menu => {
if input.is_key_pressed(KeyCode::Enter) {
self.mode = GameMode::Playing;
self.start_game(world);
}
}
GameMode::Playing => {
self.update_gameplay(dt, input, world);
if input.is_key_pressed(KeyCode::Escape) {
self.mode = GameMode::Paused;
}
}
GameMode::Paused => {
if input.is_key_pressed(KeyCode::Escape) {
self.mode = GameMode::Playing;
}
}
GameMode::GameOver => {
if input.is_key_pressed(KeyCode::Enter) {
self.mode = GameMode::Menu;
self.reset_game(world);
}
}
}
}
fn draw(&mut self, world: &World, pixels: &mut [u8]) {
// Desenho específico por modo
}
}
impl MyGame {
fn start_game(&mut self, world: &mut World) {
self.score = 0;
// Inicializa mundo
}
fn update_gameplay(&mut self, dt: f32, input: &InputHandler, world: &mut World) {
// Lógica do jogo
}
fn reset_game(&mut self, world: &mut World) {
world.game_objects.clear();
}
}
```
### 2. Estados do Jogador
```rust
#[derive(PartialEq)]
enum PlayerState {
Idle,
Walking,
Jumping,
Falling,
Attacking,
}
struct PlayerController {
state: PlayerState,
jump_timer: f32,
attack_timer: f32,
}
impl PlayerController {
fn update(&mut self, input: &InputHandler, player: &mut GameObject, dt: f32) {
match self.state {
PlayerState::Idle => {
if input.is_key_pressed(KeyCode::Space) {
self.state = PlayerState::Jumping;
self.jump_timer = 0.0;
} else if input.is_key_pressed(KeyCode::ArrowLeft)
|| input.is_key_pressed(KeyCode::ArrowRight) {
self.state = PlayerState::Walking;
}
}
PlayerState::Walking => {
// Lógica de movimento
if !input.is_key_pressed(KeyCode::ArrowLeft)
&& !input.is_key_pressed(KeyCode::ArrowRight) {
self.state = PlayerState::Idle;
}
}
PlayerState::Jumping => {
self.jump_timer += dt;
if self.jump_timer > 0.5 {
self.state = PlayerState::Falling;
}
}
PlayerState::Falling => {
// Verifica se tocou o chão
if player.transform.position.1 >= 500.0 {
self.state = PlayerState::Idle;
}
}
PlayerState::Attacking => {
self.attack_timer += dt;
if self.attack_timer > 0.3 {
self.state = PlayerState::Idle;
}
}
}
}
}
```
## Padrões de Otimização
### 1. Object Pooling
```rust
struct ObjectPool {
inactive_objects: Vec<GameObject>,
}
impl ObjectPool {
fn new() -> Self {
ObjectPool {
inactive_objects: Vec::new(),
}
}
fn get_or_create(&mut self, object_type: ObjectType) -> GameObject {
if let Some(mut obj) = self.inactive_objects.pop() {
obj.active = true;
obj.object_type = object_type;
obj
} else {
GameObject::new(0.0, 0.0, object_type)
}
}
fn return_object(&mut self, mut obj: GameObject) {
obj.active = false;
self.inactive_objects.push(obj);
}
}
```
### 2. Spatial Partitioning (Grid)
```rust
struct SpatialGrid {
cell_size: f32,
grid: HashMap<(i32, i32), Vec<usize>>, // (grid_x, grid_y) -> object indices
}
impl SpatialGrid {
fn new(cell_size: f32) -> Self {
SpatialGrid {
cell_size,
grid: HashMap::new(),
}
}
fn get_cell(&self, x: f32, y: f32) -> (i32, i32) {
((x / self.cell_size) as i32, (y / self.cell_size) as i32)
}
fn insert(&mut self, index: usize, x: f32, y: f32) {
let cell = self.get_cell(x, y);
self.grid.entry(cell).or_insert_with(Vec::new).push(index);
}
fn get_nearby(&self, x: f32, y: f32) -> Vec<usize> {
let cell = self.get_cell(x, y);
let mut nearby = Vec::new();
// Verifica célula atual e adjacentes
for dx in -1..=1 {
for dy in -1..=1 {
let check_cell = (cell.0 + dx, cell.1 + dy);
if let Some(objects) = self.grid.get(&check_cell) {
nearby.extend(objects);
}
}
}
nearby
}
}
```
## Padrões de UI
### 1. Barra de Vida Simples
```rust
fn draw_health_bar(pixels: &mut [u8], health: i32, max_health: i32, width: u32) {
let bar_width = 200;
let bar_height = 20;
let bar_x = 10;
let bar_y = 10;
let health_percent = health as f32 / max_health as f32;
let filled_width = (bar_width as f32 * health_percent) as usize;
// Desenha fundo (vermelho)
for y in bar_y..bar_y + bar_height {
for x in bar_x..bar_x + bar_width {
let index = ((y * width) + x) as usize * 4;
if index + 3 < pixels.len() {
pixels[index..index + 4].copy_from_slice(&[200, 0, 0, 255]);
}
}
}
// Desenha vida (verde)
for y in bar_y..bar_y + bar_height {
for x in bar_x..bar_x + filled_width {
let index = ((y * width) + x) as usize * 4;
if index + 3 < pixels.len() {
pixels[index..index + 4].copy_from_slice(&[0, 200, 0, 255]);
}
}
}
}
```
## Dicas Finais
1. **Mantenha o GameState Leve**: Use structs separadas para lógica complexa
2. **Evite Clones Desnecessários**: Use referências quando possível
3. **Profile Seu Código**: Use `cargo flamegraph` para encontrar gargalos
4. **Teste Incrementalmente**: Adicione features uma de cada vez
5. **Use Constantes**: Defina valores mágicos como constantes no topo do arquivo
```rust
const PLAYER_SPEED: f32 = 200.0;
const JUMP_FORCE: f32 = -500.0;
const GRAVITY: f32 = 980.0;
const MAX_ENEMIES: usize = 50;
```