# 🎮 Tutorial Completo - SevenX Engine v0.2.6
## 📚 Índice
1. [Jogo Básico - Quadrado que Move](#1-jogo-básico)
2. [Jogo de Plataforma Completo](#2-jogo-de-plataforma)
3. [Top-Down com Iluminação](#3-top-down-com-iluminação)
4. [Jogo 3D Simples](#4-jogo-3d-simples)
5. [Tower Defense com Pathfinding](#5-tower-defense)
6. [Puzzle com Efeitos](#6-puzzle-com-efeitos)
---
## 1. 🟦 Jogo Básico - Quadrado que Move
### Código Completo
```rust
use sevenx_engine::*;
use sevenx_engine::world::World;
struct MeuJogo {
player_x: f32,
player_y: f32,
}
impl GameState for MeuJogo {
fn new() -> Self {
Self {
player_x: 400.0,
player_y: 300.0,
}
}
fn update(&mut self, dt: f32, input: &InputHandler, _world: &mut World) {
let speed = 200.0;
if input.is_key_pressed(KeyCode::ArrowUp) {
self.player_y -= speed * dt;
}
if input.is_key_pressed(KeyCode::ArrowDown) {
self.player_y += speed * dt;
}
if input.is_key_pressed(KeyCode::ArrowLeft) {
self.player_x -= speed * dt;
}
if input.is_key_pressed(KeyCode::ArrowRight) {
self.player_x += speed * dt;
}
}
fn draw(&mut self, _world: &World, frame: &mut [u8]) {
// Limpa tela
for pixel in frame.chunks_exact_mut(4) {
pixel.copy_from_slice(&[40, 40, 60, 255]);
}
// Desenha jogador
let size = 20;
for y in -size..size {
for x in -size..size {
let px = (self.player_x as i32 + x) as usize;
let py = (self.player_y as i32 + y) as usize;
if px < 800 && py < 600 {
let idx = (py * 800 + px) * 4;
frame[idx] = 100;
frame[idx + 1] = 255;
frame[idx + 2] = 100;
frame[idx + 3] = 255;
}
}
}
}
}
fn main() {
Engine::new().run::<MeuJogo>();
}
```
---
## 2. 🎮 Jogo de Plataforma Completo
```rust
use sevenx_engine::*;
use sevenx_engine::world::World;
struct Plataforma {
player_x: f32,
player_y: f32,
vel_y: f32,
no_chao: bool,
plataformas: Vec<(f32, f32, f32, f32)>, // x, y, width, height
}
impl GameState for Plataforma {
fn new() -> Self {
Self {
player_x: 100.0,
player_y: 100.0,
vel_y: 0.0,
no_chao: false,
plataformas: vec![
(0.0, 550.0, 800.0, 50.0), // Chão
(200.0, 450.0, 150.0, 20.0), // Plataforma 1
(400.0, 350.0, 150.0, 20.0), // Plataforma 2
(600.0, 250.0, 150.0, 20.0), // Plataforma 3
],
}
}
fn update(&mut self, dt: f32, input: &InputHandler, _world: &mut World) {
let speed = 200.0;
let gravidade = 800.0;
let pulo = -400.0;
// Movimento horizontal
if input.is_key_pressed(KeyCode::ArrowLeft) {
self.player_x -= speed * dt;
}
if input.is_key_pressed(KeyCode::ArrowRight) {
self.player_x += speed * dt;
}
// Pulo
if input.is_key_pressed(KeyCode::Space) && self.no_chao {
self.vel_y = pulo;
self.no_chao = false;
}
// Gravidade
self.vel_y += gravidade * dt;
self.player_y += self.vel_y * dt;
// Colisão com plataformas
self.no_chao = false;
let player_size = 20.0;
for (px, py, pw, ph) in &self.plataformas {
if self.player_x + player_size > *px
&& self.player_x - player_size < px + pw
&& self.player_y + player_size > *py
&& self.player_y - player_size < py + ph
{
if self.vel_y > 0.0 {
self.player_y = py - player_size;
self.vel_y = 0.0;
self.no_chao = true;
}
}
}
// Limites da tela
self.player_x = self.player_x.clamp(20.0, 780.0);
}
fn draw(&mut self, _world: &World, frame: &mut [u8]) {
// Céu
for pixel in frame.chunks_exact_mut(4) {
pixel.copy_from_slice(&[100, 150, 255, 255]);
}
// Plataformas
for (px, py, pw, ph) in &self.plataformas {
for y in 0..*ph as i32 {
for x in 0..*pw as i32 {
let screen_x = (*px as i32 + x) as usize;
let screen_y = (*py as i32 + y) as usize;
if screen_x < 800 && screen_y < 600 {
let idx = (screen_y * 800 + screen_x) * 4;
frame[idx] = 100;
frame[idx + 1] = 50;
frame[idx + 2] = 0;
frame[idx + 3] = 255;
}
}
}
}
// Jogador
let size = 20;
for y in -size..size {
for x in -size..size {
let px = (self.player_x as i32 + x) as usize;
let py = (self.player_y as i32 + y) as usize;
if px < 800 && py < 600 {
let idx = (py * 800 + px) * 4;
frame[idx] = 255;
frame[idx + 1] = 100;
frame[idx + 2] = 100;
frame[idx + 3] = 255;
}
}
}
}
}
fn main() {
Engine::new().run::<Plataforma>();
}
```
---
## 3. 💡 Top-Down com Iluminação (v0.2.6)
```rust
use sevenx_engine::*;
use sevenx_engine::world::World;
struct TopDown {
player_x: f32,
player_y: f32,
lighting: LightingSystem,
torch_light: usize,
enemies: Vec<(f32, f32)>,
}
impl GameState for TopDown {
fn new() -> Self {
let mut lighting = LightingSystem::new();
lighting.set_ambient(10, 10, 20, 0.1);
let torch = Light::point(400.0, 300.0, 180.0)
.with_color(255, 200, 100)
.with_intensity(1.0);
let torch_light = lighting.add_light(torch);
Self {
player_x: 400.0,
player_y: 300.0,
lighting,
torch_light,
enemies: vec![
(200.0, 200.0),
(600.0, 400.0),
(300.0, 500.0),
],
}
}
fn update(&mut self, dt: f32, input: &InputHandler, _world: &mut World) {
let speed = 150.0;
if input.is_key_pressed(KeyCode::KeyW) {
self.player_y -= speed * dt;
}
if input.is_key_pressed(KeyCode::KeyS) {
self.player_y += speed * dt;
}
if input.is_key_pressed(KeyCode::KeyA) {
self.player_x -= speed * dt;
}
if input.is_key_pressed(KeyCode::KeyD) {
self.player_x += speed * dt;
}
// Atualiza luz
self.lighting.lights[self.torch_light].position = (self.player_x, self.player_y);
// Move inimigos em direção ao jogador
for (ex, ey) in &mut self.enemies {
let dx = self.player_x - *ex;
let dy = self.player_y - *ey;
let dist = (dx * dx + dy * dy).sqrt();
if dist > 50.0 {
*ex += (dx / dist) * 50.0 * dt;
*ey += (dy / dist) * 50.0 * dt;
}
}
}
fn draw(&mut self, _world: &World, frame: &mut [u8]) {
let width = 800;
let height = 600;
// Desenha chão com iluminação
for y in 0..height {
for x in 0..width {
let idx = ((y * width + x) * 4) as usize;
let base = [40, 40, 40, 255];
let lit = self.lighting.apply_lighting(x as f32, y as f32, base);
frame[idx..idx + 4].copy_from_slice(&lit);
}
}
// Desenha inimigos
for (ex, ey) in &self.enemies {
for dy in -8..8 {
for dx in -8..8 {
if dx * dx + dy * dy <= 64 {
let px = (*ex as i32 + dx) as usize;
let py = (*ey as i32 + dy) as usize;
if px < width as usize && py < height as usize {
let idx = (py * width as usize + px) * 4;
frame[idx] = 255;
frame[idx + 1] = 50;
frame[idx + 2] = 50;
frame[idx + 3] = 255;
}
}
}
}
}
// Desenha jogador
for dy in -10..10 {
for dx in -10..10 {
if dx * dx + dy * dy <= 100 {
let px = (self.player_x as i32 + dx) as usize;
let py = (self.player_y as i32 + dy) as usize;
if px < width as usize && py < height as usize {
let idx = (py * width as usize + px) * 4;
frame[idx] = 100;
frame[idx + 1] = 255;
frame[idx + 2] = 100;
frame[idx + 3] = 255;
}
}
}
}
}
}
fn main() {
Engine::new().run::<TopDown>();
}
```
---
## 4. 🎲 Jogo 3D Simples (v0.2.6)
```rust
use sevenx_engine::*;
use sevenx_engine::world::World;
struct Jogo3D {
renderer3d: Renderer3D,
cubes: Vec<Mesh3D>,
player_angle: f32,
camera_distance: f32,
}
impl GameState for Jogo3D {
fn new() -> Self {
let mut renderer3d = Renderer3D::new(800, 600);
renderer3d.camera.position = Vec3::new(0.0, 3.0, 10.0);
renderer3d.camera.look_at(Vec3::zero());
// Cria vários cubos
let mut cubes = Vec::new();
for i in 0..5 {
let mut cube = Mesh3D::cube(1.5);
cube.position = Vec3::new(
(i as f32 - 2.0) * 3.0,
0.0,
0.0,
);
cubes.push(cube);
}
Self {
renderer3d,
cubes,
player_angle: 0.0,
camera_distance: 10.0,
}
}
fn update(&mut self, dt: f32, input: &InputHandler, _world: &mut World) {
// Rotaciona cubos
for (i, cube) in self.cubes.iter_mut().enumerate() {
cube.rotation.y += dt * (1.0 + i as f32 * 0.2);
cube.rotation.x += dt * 0.5;
}
// Controla câmera
if input.is_key_pressed(KeyCode::ArrowLeft) {
self.player_angle += 2.0 * dt;
}
if input.is_key_pressed(KeyCode::ArrowRight) {
self.player_angle -= 2.0 * dt;
}
if input.is_key_pressed(KeyCode::ArrowUp) {
self.camera_distance -= 5.0 * dt;
}
if input.is_key_pressed(KeyCode::ArrowDown) {
self.camera_distance += 5.0 * dt;
}
self.camera_distance = self.camera_distance.clamp(5.0, 20.0);
// Atualiza posição da câmera
self.renderer3d.camera.position = Vec3::new(
self.player_angle.sin() * self.camera_distance,
3.0,
self.player_angle.cos() * self.camera_distance,
);
self.renderer3d.camera.look_at(Vec3::zero());
}
fn draw(&mut self, _world: &World, frame: &mut [u8]) {
// Céu gradiente
for y in 0..600 {
for x in 0..800 {
let idx = (y * 800 + x) * 4;
let gradient = (y as f32 / 600.0 * 100.0) as u8;
frame[idx] = 20 + gradient / 2;
frame[idx + 1] = 20 + gradient / 2;
frame[idx + 2] = 40 + gradient;
frame[idx + 3] = 255;
}
}
// Renderiza cubos 3D
for cube in &self.cubes {
self.renderer3d.render_mesh(cube, frame, 800, 600);
}
}
}
fn main() {
let config = EngineConfig::default()
.with_title("Jogo 3D - Use Setas")
.with_size(800, 600);
Engine::with_config(config).run::<Jogo3D>();
}
```
---
## 5. 🗼 Tower Defense com Pathfinding (v0.2.6)
```rust
use sevenx_engine::*;
use sevenx_engine::world::World;
struct TowerDefense {
pathfinder: Pathfinder,
enemies: Vec<Enemy>,
towers: Vec<(i32, i32)>,
spawn_timer: f32,
cell_size: i32,
}
struct Enemy {
pos: GridPos,
path: Vec<GridPos>,
path_index: usize,
move_timer: f32,
}
impl GameState for TowerDefense {
fn new() -> Self {
let mut pathfinder = Pathfinder::new(40, 30);
// Cria caminho com obstáculos
for y in 5..25 {
pathfinder.add_obstacle(20, y);
}
for x in 5..20 {
pathfinder.add_obstacle(x, 15);
}
Self {
pathfinder,
enemies: Vec::new(),
towers: vec![(10, 10), (30, 20)],
spawn_timer: 0.0,
cell_size: 20,
}
}
fn update(&mut self, dt: f32, _input: &InputHandler, _world: &mut World) {
// Spawn inimigos
self.spawn_timer += dt;
if self.spawn_timer > 2.0 {
self.spawn_timer = 0.0;
let start = GridPos::new(0, 15);
let goal = GridPos::new(39, 15);
if let Some(path) = self.pathfinder.find_path(start, goal) {
self.enemies.push(Enemy {
pos: start,
path,
path_index: 0,
move_timer: 0.0,
});
}
}
// Move inimigos
for enemy in &mut self.enemies {
enemy.move_timer += dt;
if enemy.move_timer > 0.3 && enemy.path_index < enemy.path.len() - 1 {
enemy.move_timer = 0.0;
enemy.path_index += 1;
enemy.pos = enemy.path[enemy.path_index];
}
}
// Remove inimigos que chegaram ao fim
self.enemies.retain(|e| e.path_index < e.path.len() - 1);
}
fn draw(&mut self, _world: &World, frame: &mut [u8]) {
let width = 800;
// Limpa tela
for pixel in frame.chunks_exact_mut(4) {
pixel.copy_from_slice(&[40, 80, 40, 255]);
}
// Desenha grid
for y in 0..self.pathfinder.grid_height {
for x in 0..self.pathfinder.grid_width {
let pos = GridPos::new(x, y);
let px = x * self.cell_size;
let py = y * self.cell_size;
let color = if self.pathfinder.obstacles.contains(&pos) {
[100, 50, 50, 255]
} else {
[60, 100, 60, 255]
};
for dy in 1..(self.cell_size - 1) {
for dx in 1..(self.cell_size - 1) {
let sx = (px + dx) as usize;
let sy = (py + dy) as usize;
if sx < width as usize && sy < 600 {
let idx = (sy * width as usize + sx) * 4;
frame[idx..idx + 4].copy_from_slice(&color);
}
}
}
}
}
// Desenha torres
for (tx, ty) in &self.towers {
let px = tx * self.cell_size;
let py = ty * self.cell_size;
for dy in 0..self.cell_size {
for dx in 0..self.cell_size {
let sx = (px + dx) as usize;
let sy = (py + dy) as usize;
if sx < width as usize && sy < 600 {
let idx = (sy * width as usize + sx) * 4;
frame[idx] = 100;
frame[idx + 1] = 100;
frame[idx + 2] = 255;
frame[idx + 3] = 255;
}
}
}
}
// Desenha inimigos
for enemy in &self.enemies {
let px = enemy.pos.x * self.cell_size + self.cell_size / 2;
let py = enemy.pos.y * self.cell_size + self.cell_size / 2;
for dy in -6..6 {
for dx in -6..6 {
if dx * dx + dy * dy <= 36 {
let sx = (px + dx) as usize;
let sy = (py + dy) as usize;
if sx < width as usize && sy < 600 {
let idx = (sy * width as usize + sx) * 4;
frame[idx] = 255;
frame[idx + 1] = 50;
frame[idx + 2] = 50;
frame[idx + 3] = 255;
}
}
}
}
}
}
}
fn main() {
let config = EngineConfig::default()
.with_title("Tower Defense")
.with_size(800, 600);
Engine::with_config(config).run::<TowerDefense>();
}
```
---
## 6. 🧩 Puzzle com Efeitos (v0.2.6)
```rust
use sevenx_engine::*;
use sevenx_engine::world::World;
struct Puzzle {
grid: Vec<Vec<u8>>,
selected: Option<(usize, usize)>,
post: PostProcessor,
score: u32,
input_cooldown: f32,
}
impl GameState for Puzzle {
fn new() -> Self {
let mut post = PostProcessor::new();
post.add_effect(PostEffect::ColorGrading {
contrast: 1.2,
saturation: 1.3,
brightness: 0.05,
});
// Grid 8x8 com cores aleatórias (0-4)
let mut grid = vec![vec![0u8; 8]; 8];
for y in 0..8 {
for x in 0..8 {
grid[y][x] = (x + y) as u8 % 5;
}
}
Self {
grid,
selected: None,
post,
score: 0,
input_cooldown: 0.0,
}
}
fn update(&mut self, dt: f32, input: &InputHandler, _world: &mut World) {
self.input_cooldown -= dt;
if self.input_cooldown <= 0.0 {
// Seleciona peça
if input.is_key_pressed(KeyCode::Space) {
self.selected = Some((4, 4));
self.input_cooldown = 0.2;
}
// Move seleção
if let Some((x, y)) = self.selected {
let mut moved = false;
let mut new_x = x;
let mut new_y = y;
if input.is_key_pressed(KeyCode::ArrowUp) && y > 0 {
new_y = y - 1;
moved = true;
}
if input.is_key_pressed(KeyCode::ArrowDown) && y < 7 {
new_y = y + 1;
moved = true;
}
if input.is_key_pressed(KeyCode::ArrowLeft) && x > 0 {
new_x = x - 1;
moved = true;
}
if input.is_key_pressed(KeyCode::ArrowRight) && x < 7 {
new_x = x + 1;
moved = true;
}
if moved {
// Troca peças
let temp = self.grid[y][x];
self.grid[y][x] = self.grid[new_y][new_x];
self.grid[new_y][new_x] = temp;
self.selected = Some((new_x, new_y));
self.score += 10;
self.input_cooldown = 0.2;
}
}
}
}
fn draw(&mut self, _world: &World, frame: &mut [u8]) {
let width = 800;
let cell_size = 80;
let offset_x = 100;
let offset_y = 50;
// Fundo
for pixel in frame.chunks_exact_mut(4) {
pixel.copy_from_slice(&[30, 30, 50, 255]);
}
// Desenha grid
for y in 0..8 {
for x in 0..8 {
let px = offset_x + x * cell_size;
let py = offset_y + y * cell_size;
let color = match self.grid[y][x] {
0 => [255, 100, 100, 255],
1 => [100, 255, 100, 255],
2 => [100, 100, 255, 255],
3 => [255, 255, 100, 255],
_ => [255, 100, 255, 255],
};
// Destaque se selecionado
let is_selected = self.selected == Some((x, y));
let border = if is_selected { 5 } else { 2 };
for dy in border..(cell_size - border) {
for dx in border..(cell_size - border) {
let sx = (px + dx) as usize;
let sy = (py + dy) as usize;
if sx < width as usize && sy < 600 {
let idx = (sy * width as usize + sx) * 4;
frame[idx..idx + 4].copy_from_slice(&color);
}
}
}
}
}
// Aplica post-processing
self.post.apply(frame, width, 600);
}
}
fn main() {
let config = EngineConfig::default()
.with_title("Puzzle Game - Space para selecionar, Setas para mover")
.with_size(800, 600);
Engine::with_config(config).run::<Puzzle>();
}
```
---
## 🎯 Executar os Jogos
Salve cada código em `src/main.rs` e execute:
```bash
cargo run --release
```
## 📚 Próximos Passos
- Adicione sons com `AudioSystem`
- Use sprites com `ResourceManager`
- Salve progresso com `GameSave`
- Adicione mais níveis!
**Divirta-se criando! 🎮**