Repository: CoininDev/aquatunnel
Pull request #1: Macroquad
Author: login: CoininDev
Created: 2025-04-30T20:56:48Z | Merged: 2025-04-30T21:02:26Z

Using macroquad for rendering and input handling

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced asynchronous texture loading and resource management for improved performance.
  - Added support for z-order rendering, allowing layered display of sprites and backgrounds.
  - Implemented smooth camera tracking and improved camera movement.

- **Bug Fixes**
  - Improved input handling with more responsive keyboard and mouse controls.

- **Refactor**
  - Migrated graphics and input systems from SDL2 to macroquad for rendering and input.
  - Simplified game loop and resource setup with async/await.
  - Removed all physics simulation and related components.

- **Chores**
  - Updated dependencies, removing unused crates and adding macroquad and futures.
  - Removed obsolete files and modules related to SDL2 and physics.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/Cargo.lock b/Cargo.lock
index 025f6cf..5613f2d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,12 @@
 # It is not intended for manual editing.
 version = 4
 
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
 [[package]]
 name = "allocator-api2"
 version = "0.2.21"
@@ -28,12 +34,11 @@ name = "aquatunnel"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "glam",
+ "futures",
  "legion",
- "miniquad",
+ "macroquad",
  "nalgebra",
  "rapier2d",
- "sdl2",
 ]
 
 [[package]]
@@ -93,12 +98,33 @@ version = "1.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
 
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "crossbeam"
 version = "0.8.4"
@@ -197,12 +223,130 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "flate2"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
 [[package]]
 name = "foldhash"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
 
+[[package]]
+name = "fontdue"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e57e16b3fe8ff4364c0661fdaac543fb38b29ea9bc9c2f45612d90adf931d2b"
+dependencies = [
+ "hashbrown",
+ "ttf-parser",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
 [[package]]
 name = "getrandom"
 version = "0.2.15"
@@ -216,9 +360,9 @@ dependencies = [
 
 [[package]]
 name = "glam"
-version = "0.30.1"
+version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf3aa70d918d2b234126ff4f850f628f172542bf0603ded26b8ee36e5e22d5f9"
+checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
 
 [[package]]
 name = "hashbrown"
@@ -231,6 +375,19 @@ dependencies = [
  "foldhash",
 ]
 
+[[package]]
+name = "image"
+version = "0.24.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "num-traits",
+ "png",
+]
+
 [[package]]
 name = "instant"
 version = "0.1.13"
@@ -249,12 +406,6 @@ dependencies = [
  "either",
 ]
 
-[[package]]
-name = "lazy_static"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-
 [[package]]
 name = "legion"
 version = "0.4.0"
@@ -318,6 +469,26 @@ version = "0.4.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 
+[[package]]
+name = "macroquad"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2befbae373456143ef55aa93a73594d080adfb111dc32ec96a1123a3e4ff4ae"
+dependencies = [
+ "fontdue",
+ "glam",
+ "image",
+ "macroquad_macro",
+ "miniquad",
+ "quad-rand",
+]
+
+[[package]]
+name = "macroquad_macro"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64b1d96218903768c1ce078b657c0d5965465c95a60d2682fd97443c9d2483dd"
+
 [[package]]
 name = "malloc_buf"
 version = "0.0.6"
@@ -337,6 +508,12 @@ dependencies = [
  "rawpointer",
 ]
 
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
 [[package]]
 name = "miniquad"
 version = "0.4.8"
@@ -349,6 +526,16 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "miniz_oxide"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
 [[package]]
 name = "nalgebra"
 version = "0.33.2"
@@ -515,6 +702,31 @@ version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
 
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.94"
@@ -543,6 +755,12 @@ dependencies = [
  "syn 2.0.100",
 ]
 
+[[package]]
+name = "quad-rand"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a651516ddc9168ebd67b24afd085a718be02f8858fe406591b013d101ce2f40"
+
 [[package]]
 name = "quote"
 version = "1.0.40"
@@ -644,29 +862,6 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
-[[package]]
-name = "sdl2"
-version = "0.37.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380"
-dependencies = [
- "bitflags 1.3.2",
- "lazy_static",
- "libc",
- "sdl2-sys",
-]
-
-[[package]]
-name = "sdl2-sys"
-version = "0.37.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3"
-dependencies = [
- "cfg-if",
- "libc",
- "version-compare",
-]
-
 [[package]]
 name = "serde"
 version = "1.0.219"
@@ -700,6 +895,12 @@ dependencies = [
  "wide",
 ]
 
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -789,6 +990,12 @@ dependencies = [
  "syn 2.0.100",
 ]
 
+[[package]]
+name = "ttf-parser"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
+
 [[package]]
 name = "typenum"
 version = "1.18.0"
@@ -811,12 +1018,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "version-compare"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
-
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
diff --git a/Cargo.toml b/Cargo.toml
index a02391d..d471996 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,9 +5,8 @@ edition = "2024"
 
 [dependencies]
 anyhow = "1.0.97"
-glam = "0.30.1"
+futures = "0.3.31"
 legion = "0.4.0"
-miniquad = "0.4.8"
+macroquad = "0.4.14"
 nalgebra = "0.33.2"
 rapier2d = "0.24.0"
-sdl2 = {version = "0.37.0", features = ["ttf", "image"]}
diff --git a/src/comps.rs b/src/comps.rs
index 4014db3..d8715bc 100644
--- a/src/comps.rs
+++ b/src/comps.rs
@@ -1,17 +1,17 @@
+use macroquad::{
+    color::Color,
+    math::{IVec4, Vec2, vec2},
+};
 use std::collections::HashMap;
 
-use glam::{IVec4, Vec2, vec2};
-use rapier2d::prelude::{ColliderHandle, RigidBodyHandle};
-use sdl2::{pixels::Color, rect::FPoint};
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct Transform {
     pub position: Vec2,
     pub scale: Vec2,
     pub rotation: f32,
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct Player {
     pub speed: f32,
 }
@@ -26,62 +26,31 @@ impl Default for Transform {
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct Sprite {
     pub image_path: String,
+    pub z_order: f32,
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct DebugSprite {
-    pub size: FPoint,
+    pub size: Vec2,
     pub color: Color,
+    pub z_order: f32,
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct Spritesheet {
     pub animations: HashMap<String, Vec<IVec4>>,
     pub image_path: String,
     pub dst_size: Vec2,
+    pub z_order: f32,
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
 pub struct AnimationPlayer {
     pub current_animation: String,
     pub current_frame: usize,
     pub playing: bool,
     pub frame_duration: f32,
 }
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct DynamicBody {
-    pub handle: Option<RigidBodyHandle>,
-    pub collider_handle: Option<ColliderHandle>,
-    pub size: Vec2,
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct StaticBody {
-    pub handle: Option<RigidBodyHandle>,
-    pub collider_handle: Option<ColliderHandle>,
-    pub size: Vec2,
-}
-
-impl DynamicBody {
-    pub fn new(size: Vec2) -> Self {
-        DynamicBody {
-            handle: None,
-            collider_handle: None,
-            size,
-        }
-    }
-}
-
-impl StaticBody {
-    pub fn new(size: Vec2) -> Self {
-        StaticBody {
-            handle: None,
-            collider_handle: None,
-            size,
-        }
-    }
-}
diff --git a/src/entitites.rs b/src/entitites.rs
index c40fb05..1844ea0 100644
--- a/src/entitites.rs
+++ b/src/entitites.rs
@@ -1,9 +1,11 @@
 use std::collections::HashMap;
 
 use crate::comps::*;
-use glam::{IVec4, Vec2};
 use legion::World;
-use sdl2::{pixels::Color, rect::FPoint};
+use macroquad::{
+    color,
+    math::{IVec4, Vec2},
+};
 
 pub fn populate(world: &mut World) {
     let player_anims = animations();
@@ -17,6 +19,7 @@ pub fn populate(world: &mut World) {
             image_path: "assets/C3ZwL.png".to_string(),
             animations: player_anims,
             dst_size: Vec2::new(0.64, 0.64),
+            z_order: 2.,
         },
         AnimationPlayer {
             current_animation: String::from("right"),
@@ -24,23 +27,24 @@ pub fn populate(world: &mut World) {
             playing: true,
             frame_duration: 0.1,
         },
-        Player { speed: 1.0 },
-        DynamicBody::new(Vec2::new(0.32, 0.64)),
-        DebugSprite {
-            size: FPoint::new(0.32, 0.64),
-            color: Color::CYAN,
-        },
+        Player { speed: 0.7 },
+        // DebugSprite {
+        //     size: Vec2::new(0.32, 0.64),
+        //     color: color::SKYBLUE,
+        //     z_order: 0.1,
+        // },
     ));
+
     //ground
     world.push((
         Transform {
             position: Vec2::new(4.0, 5.0),
             ..Default::default()
         },
-        StaticBody::new(Vec2::new(1.0, 1.0)),
         DebugSprite {
-            size: FPoint::new(1.0, 1.0),
-            color: Color::WHITE,
+            size: Vec2::new(1.0, 1.0),
+            color: color::WHITE,
+            z_order: -1.,
         },
     ));
 }
@@ -105,4 +109,3 @@ fn animations() -> HashMap<String, Vec<IVec4>> {
     );
     cu
 }
-
diff --git a/src/game.rs b/src/game.rs
index 4222888..831accc 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -1,101 +1,85 @@
-use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc, time::Instant};
+use std::{collections::HashMap, sync::Arc};
 
-use legion::{Resources, Schedule, World, systems::CommandBuffer};
-use sdl2::{EventPump, event::Event, keyboard::Keycode, render::Texture};
+use legion::{Entity, Resources, Schedule, World, systems::CommandBuffer};
+use macroquad::{
+    camera::Camera2D,
+    input::{KeyCode, is_key_down},
+    math::Vec2,
+    texture::Texture2D,
+    time::get_frame_time,
+    window::next_frame,
+};
 
 use crate::{
+    comps::Transform,
     entitites::populate,
     input::{InputContext, InputSetup},
-    physics::PhysicsContext,
-    window::init_sdl,
+    load::load,
     sys::{
-        load::{load_physics_system, load_spritesheet_system, load_system},
-        render, tick,
+        render::{self, camera_system, camera_ui_system},
+        tick,
     },
 };
 
 pub struct Time {
-    pub last: Instant,
     pub delta: f32,
 }
 
-pub fn run_game() -> Result<(), String> {
-    let (canvas, texture_creator, event_pump) = init_sdl()?;
-    let event_pump = Rc::new(RefCell::new(event_pump));
-    let textures: HashMap<String, Arc<Texture<'_>>> = HashMap::new();
+pub struct Track {
+    pub pos: Vec2,
+}
+
+pub async fn run_game() -> Result<(), String> {
+    let textures: HashMap<String, Arc<Texture2D>> = HashMap::new();
     let mut world = World::default();
     let mut resources = Resources::default();
-
-    let ttf_ctx = Arc::new(sdl2::ttf::init().unwrap());
-    let time = Time {
-        last: Instant::now(),
-        delta: 0.0,
-    };
-
-    let input_ctx = InputContext::new(event_pump.clone(), InputSetup::default());
-    let physics_ctx = PhysicsContext::default();
-
+    let camera = Box::new(Camera2D::default());
+    let input_ctx = InputContext::new(InputSetup::default());
+    let track: Track = Track { pos: Vec2::ZERO };
+    resources.insert(track);
     resources.insert(textures);
-    resources.insert(canvas);
-    resources.insert(texture_creator);
     resources.insert(input_ctx);
-    resources.insert(physics_ctx);
-    resources.insert(ttf_ctx);
-    resources.insert(time);
+    resources.insert(camera);
     resources.insert(CommandBuffer::new(&world));
 
     populate(&mut world);
 
-    let mut load_schedule = Schedule::builder()
-        .add_thread_local(load_system())
-        .add_thread_local(load_spritesheet_system())
-        .add_system(load_physics_system())
-        .build();
+    // Systems involving macroquad rendering or input requires local thread
     let mut step_schedule = Schedule::builder()
-        .add_system(tick::time_update_system())
         .add_thread_local(tick::input_update_system())
         .add_system(tick::step_animation_system(0.0))
-        .add_system(tick::step_physics_system())
-        .add_system(tick::physics_integration_system())
+        .add_system(render::z_y_axis_player_system())
         .flush()
-        .add_thread_local(tick::move_player_system())
+        .add_system(tick::move_player_system())
+        .add_system(render::track_player_system())
+        .add_thread_local(tick::animate_player_system())
         .build();
 
     let mut draw_schedule = Schedule::builder()
+        .add_thread_local(camera_system())
         .add_thread_local(render::clear_screen_system())
         .add_thread_local(render::render_system())
-        .add_thread_local(render::draw_fps_system(
-            0,
-            String::from("0"),
-            Instant::now(),
-        ))
-        .add_thread_local(render::present_system())
+        .flush()
+        .add_thread_local(camera_ui_system())
+        .add_thread_local(render::draw_fps_system())
         .build();
 
-    load_schedule.execute(&mut world, &mut resources);
+    load(&mut world, &mut resources).await;
     'running: loop {
-        if check_exit(event_pump.clone()) {
+        let dt = Time {
+            delta: get_frame_time(),
+        };
+        resources.insert(dt);
+
+        if is_key_down(KeyCode::Escape) {
             break 'running;
         }
+
         step_schedule.execute(&mut world, &mut resources);
         draw_schedule.execute(&mut world, &mut resources);
-    }
-
-    Ok(())
-}
-
-fn check_exit(event_pump: Rc<RefCell<EventPump>>) -> bool {
-    for event in event_pump.borrow_mut().poll_iter() {
-        match event {
-            Event::Quit { .. }
-            | Event::KeyDown {
-                keycode: Some(Keycode::ESCAPE),
-                ..
-            } => return true,
 
-            _ => {}
-        }
+        next_frame().await
     }
 
-    return false;
+    Ok(())
 }
diff --git a/src/input.rs b/src/input.rs
index 2ee2e84..2318ba8 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -1,67 +1,91 @@
-use std::{
-    cell::RefCell,
-    collections::{HashMap, VecDeque},
-    rc::Rc,
-};
+use std::collections::{HashMap, VecDeque};
 
-use glam::Vec2;
-use sdl2::{EventPump, event::Event, keyboard::Keycode};
+use macroquad::input::{KeyCode, MouseButton, is_key_down};
+use macroquad::input::{mouse_delta_position, mouse_position};
+use macroquad::math::Vec2;
+use macroquad::window::screen_width;
 
 pub struct InputContext {
     pub move_direction: Vec2,
     pub look_direction: Vec2,
     pub actions: VecDeque<InputAction>,
-    setup: InputSetup,
-    event_pump: Rc<RefCell<EventPump>>,
+    pub setup: InputSetup,
 }
 
 impl InputContext {
-    pub fn new(event_pump: Rc<RefCell<EventPump>>, setup: InputSetup) -> Self {
+    pub fn new(setup: InputSetup) -> Self {
         InputContext {
             move_direction: Vec2::ZERO,
             look_direction: Vec2::ZERO,
             actions: VecDeque::new(),
             setup,
-            event_pump,
         }
     }
 
     pub fn update(&mut self) {
-        // Essa função deve ler inputs brutos e atualizar as informações move_direction,
-        // look_direction e actions, para serem exportadas à lógica do jogo.
-
-        for event in self.event_pump.borrow_mut().poll_iter() {
-            match event {
-                Event::KeyDown {
-                    keycode: Some(k), ..
-                } => {
-                    if let Some(action) = self.setup.keybindings.get(&RawAction::Key(k)) {
-                        self.actions.push_back(action.clone());
-                    }
-                }
-
-                _ => {}
-            }
-        }
+        self.move_direction = match self.setup.move_method {
+            0 => self.method0(),
+            1 => self.method1(),
+            2 => self.method2(),
+            3 => self.method3(),
+            4 => self.method4(),
+            5 => self.method5(),
+            _ => Vec2::ZERO,
+        };
 
-        match self.setup.move_method {
-            0 => self.move_direction = self.method_0(),
-            _ => {}
-        }
+        self.look_direction = match self.setup.look_method {
+            0 => self.method0(),
+            1 => self.method1(),
+            2 => self.method2(),
+            3 => self.method3(),
+            4 => self.method4(),
+            5 => self.method5(),
+            _ => Vec2::ZERO,
+        };
     }
 
-    fn method_0(&self) -> Vec2 {
-        let event_pump_ref = self.event_pump.borrow_mut();
-        let keyboard_state = event_pump_ref.keyboard_state();
+    // WASD
+    fn method0(&self) -> Vec2 {
         // -A +D
-        let x_signal = -(keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::A) as i32)
-            + (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::D) as i32);
+        let x_signal = -(is_key_down(KeyCode::A) as i32) + (is_key_down(KeyCode::D) as i32);
         // -W +S
-        let y_signal = -(keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::W) as i32)
-            + (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::S) as i32);
+        let y_signal = -(is_key_down(KeyCode::W) as i32) + (is_key_down(KeyCode::S) as i32);
 
         Vec2::new(x_signal as f32, y_signal as f32).normalize_or_zero()
     }
+
+    // Direção do centro da tela pro mouse
+    fn method1(&self) -> Vec2 {
+        let (mouse_x, mouse_y) = mouse_position();
+        let center_x = screen_width() / 2.0;
+        let center_y = screen_width() / 2.0;
+
+        let direction = Vec2::new(mouse_x - center_x, mouse_y - center_y);
+        direction.normalize_or_zero()
+    }
+
+    // Setas do teclado
+    fn method2(&self) -> Vec2 {
+        // -left +right
+        let x_signal = -(is_key_down(KeyCode::Left) as i32) + (is_key_down(KeyCode::Right) as i32);
+        // -up +down
+        let y_signal = -(is_key_down(KeyCode::Up) as i32) + (is_key_down(KeyCode::Down) as i32);
+
+        Vec2::new(x_signal as f32, y_signal as f32).normalize_or_zero()
+    }
+
+    // Direção delta do mouse
+    fn method3(&self) -> Vec2 {
+        mouse_delta_position().normalize_or_zero()
+    }
+    // Joystick esquerdo
+    fn method4(&self) -> Vec2 {
+        todo!()
+    }
+    // Joystick direito
+    fn method5(&self) -> Vec2 {
+        todo!()
+    }
 }
 
 #[derive(Clone, Copy)]
@@ -69,13 +93,15 @@ pub enum InputAction {
     DebugAction,
 }
 
-#[derive(PartialEq, Eq, Hash)]
+#[derive(PartialEq, Eq, Hash, Clone)]
 pub enum RawAction {
-    Key(Keycode),
+    Key(KeyCode),
+    MouseButton(MouseButton),
 }
 
+#[derive(Clone)]
 pub struct InputSetup {
-    keybindings: HashMap<RawAction, InputAction>,
+    pub keybindings: HashMap<RawAction, InputAction>,
     move_method: u8,
     look_method: u8,
 }
@@ -83,7 +109,11 @@ pub struct InputSetup {
 impl Default for InputSetup {
     fn default() -> Self {
         let mut keybindings = HashMap::new();
-        keybindings.insert(RawAction::Key(Keycode::D), InputAction::DebugAction);
+        keybindings.insert(RawAction::Key(KeyCode::D), InputAction::DebugAction);
+        keybindings.insert(
+            RawAction::MouseButton(MouseButton::Left),
+            InputAction::DebugAction,
+        );
 
         InputSetup {
             keybindings,
diff --git a/src/load.rs b/src/load.rs
new file mode 100644
index 0000000..8837616
--- /dev/null
+++ b/src/load.rs
@@ -0,0 +1,40 @@
+use std::{collections::HashMap, sync::Arc};
+
+use futures::future::join_all;
+use legion::{Resources, World, query::*};
+use macroquad::texture::{Texture2D, load_texture};
+
+use crate::comps::{Sprite, Spritesheet};
+
+pub async fn load(world: &mut World, resources: &mut Resources) {
+    let mut textures = resources
+        .get_mut::<HashMap<String, Arc<Texture2D>>>()
+        .unwrap();
+
+    let mut img_paths: Vec<String> = Vec::new();
+    let mut query = <&Sprite>::query();
+    for spr in query.iter(world) {
+        if !textures.contains_key(&spr.image_path) {
+            img_paths.push(spr.image_path.clone());
+        }
+    }
+    let mut query = <&Spritesheet>::query();
+    for spr in query.iter(world) {
+        if !textures.contains_key(&spr.image_path) {
+            img_paths.push(spr.image_path.clone());
+        }
+    }
+
+    let futures = img_paths.iter().map(|path| async move {
+        let tex = load_texture(path).await.unwrap();
+        (path.clone(), Arc::new(tex))
+    });
+
+    let loaded_texs: Vec<(String, Arc<Texture2D>)> = join_all(futures).await;
+
+    for (path, tex) in loaded_texs {
+        tex.set_filter(macroquad::texture::FilterMode::Nearest);
+        textures.insert(path.clone(), tex);
+        println!("imagem {} carregada.", path);
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 48df741..774dc58 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,12 +2,12 @@ mod comps;
 mod entitites;
 mod game;
 mod input;
-mod physics;
-mod window;
+mod load;
 mod sys;
 
-fn main() {
-    if let Err(err) = game::run_game() {
+#[macroquad::main("Lo")]
+async fn main() {
+    if let Err(err) = game::run_game().await {
         println!("{}", err);
     }
 }
diff --git a/src/physics.rs b/src/physics.rs
deleted file mode 100644
index 5a93477..0000000
--- a/src/physics.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use rapier2d::prelude::*;
-
-pub struct PhysicsContext {
-    pub pipeline: PhysicsPipeline,
-    pub bodies: RigidBodySet,
-    pub colliders: ColliderSet,
-    pub islands: IslandManager,
-    pub impulse_joints: ImpulseJointSet,
-    pub multibody_joints: MultibodyJointSet,
-    pub gravity: Vector<Real>,
-    pub integration_params: IntegrationParameters,
-    pub broad_phase: DefaultBroadPhase,
-    pub narrow_phase: NarrowPhase,
-    pub ccd_solver: CCDSolver,
-    pub hooks: (),
-    pub events: (),
-}
-
-impl Default for PhysicsContext {
-    fn default() -> Self {
-        PhysicsContext {
-            pipeline: PhysicsPipeline::new(),
-            bodies: RigidBodySet::default(),
-            colliders: ColliderSet::default(),
-            gravity: vector![0.0, 1.0],
-            islands: IslandManager::default(),
-            impulse_joints: ImpulseJointSet::default(),
-            multibody_joints: MultibodyJointSet::default(),
-            integration_params: IntegrationParameters::default(),
-            broad_phase: DefaultBroadPhase::default(),
-            narrow_phase: NarrowPhase::default(),
-            ccd_solver: CCDSolver::default(),
-            hooks: (),
-            events: (),
-        }
-    }
-}
diff --git a/src/sys/load.rs b/src/sys/load.rs
deleted file mode 100644
index b37cca5..0000000
--- a/src/sys/load.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-use std::{collections::HashMap, sync::Arc};
-
-use crate::{comps::*, physics::PhysicsContext};
-use legion::{world::SubWorld, *};
-use rapier2d::{
-    na::vector,
-    prelude::{ColliderBuilder, RigidBodyBuilder},
-};
-use sdl2::{
-    image::LoadTexture as _,
-    render::{Texture, TextureCreator},
-    video::WindowContext,
-};
-
-#[system(for_each)]
-pub fn load(
-    sprite: &Sprite,
-    #[resource] textures: &mut HashMap<String, Arc<Texture<'static>>>,
-    #[resource] texture_creator: &TextureCreator<WindowContext>,
-) {
-    if !textures.contains_key(&sprite.image_path) {
-        println!("cadastrando sprite '{}' em textures", sprite.image_path);
-
-        let tex = texture_creator
-            .load_texture(&sprite.image_path)
-            .expect("Erro ao carregar textura");
-        textures.insert(
-            sprite.image_path.clone(),
-            Arc::new(unsafe { std::mem::transmute(tex) }),
-        );
-    }
-}
-
-#[system(for_each)]
-pub fn load_spritesheet(
-    sprite: &Spritesheet,
-    #[resource] textures: &mut HashMap<String, Arc<Texture<'static>>>,
-    #[resource] texture_creator: &TextureCreator<WindowContext>,
-) {
-    if !textures.contains_key(&sprite.image_path) {
-        println!(
-            "cadastrando spritesheet '{}' em textures",
-            sprite.image_path
-        );
-        let tex = texture_creator
-            .load_texture(&sprite.image_path)
-            .expect("Erro ao carregar textura");
-        textures.insert(
-            sprite.image_path.clone(),
-            Arc::new(unsafe { std::mem::transmute(tex) }),
-        );
-    }
-}
-
-#[system]
-#[write_component(DynamicBody)]
-#[write_component(StaticBody)]
-#[read_component(Transform)]
-pub fn load_physics(world: &mut SubWorld, #[resource] physics: &mut PhysicsContext) {
-    let mut dyn_query = <(&Transform, &mut DynamicBody)>::query();
-    for (transform, body) in dyn_query.iter_mut(world) {
-        let rb = RigidBodyBuilder::dynamic()
-            .translation(vector![transform.position.x, transform.position.y])
-            .rotation(transform.rotation as f32)
-            .build();
-
-        let handle = physics.bodies.insert(rb);
-        body.handle = Some(handle);
-        let collider = ColliderBuilder::cuboid(
-            body.size.x,// * transform.scale.x,
-            body.size.y,// * transform.scale.y,
-        )
-        .build();
-
-        physics
-            .colliders
-            .insert_with_parent(collider, handle, &mut physics.bodies);
-    }
-
-    let mut stt_query = <(&Transform, &mut StaticBody)>::query();
-    for (transform, body) in stt_query.iter_mut(world) {
-        let rb = RigidBodyBuilder::fixed()
-            .translation(vector![transform.position.x, transform.position.y])
-            .rotation(transform.rotation as f32)
-            .build();
-
-        let handle = physics.bodies.insert(rb);
-        body.handle = Some(handle);
-        let collider = ColliderBuilder::cuboid(
-            body.size.x,// * transform.scale.x,
-            body.size.y,// * transform.scale.y,
-        )
-        .build();
-        println!("{}, {}", 
-            body.size.x * transform.scale.x,
-            body.size.y * transform.scale.y,
-        );
-
-        physics
-            .colliders
-            .insert_with_parent(collider, handle, &mut physics.bodies);
-    }
-}
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index 3dac7f4..d3c581c 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -1,3 +1,2 @@
 pub mod render;
-pub mod load;
-pub mod tick;
\ No newline at end of file
+pub mod tick;
diff --git a/src/sys/render.rs b/src/sys/render.rs
index 8af5d00..f1ce604 100644
--- a/src/sys/render.rs
+++ b/src/sys/render.rs
@@ -1,61 +1,69 @@
-use std::{
-    collections::HashMap,
-    sync::Arc,
-    time::{Duration, Instant},
+use legion::{world::SubWorld, *};
+use macroquad::{
+    color::{self, *},
+    math::*,
+    prelude::*,
 };
+use std::{collections::HashMap, sync::Arc};
 
-use glam::Vec2;
-use legion::{world::SubWorld, *};
-use sdl2::{
-    pixels::Color,
-    rect::{FRect, Rect},
-    render::{Texture, TextureCreator, WindowCanvas},
-    ttf::Sdl2TtfContext,
-    video::WindowContext,
+use crate::{
+    comps::{AnimationPlayer, DebugSprite, Player, Sprite, Spritesheet, Transform},
+    game::{Time, Track},
 };
 
-use crate::comps::{AnimationPlayer, DebugSprite, DynamicBody, Sprite, Spritesheet, StaticBody, Transform};
+#[system]
+pub fn clear_screen() {
+    clear_background(DARKPURPLE);
+}
 
 #[system]
-pub fn clear_screen(#[resource] canvas: &mut WindowCanvas) {
-    canvas.set_draw_color(Color::BLACK);
-    canvas.clear();
+pub fn draw_fps() {
+    draw_text(format!("FPS: {}", get_fps()).as_str(), 4., 24., 24., WHITE);
+}
+
+#[system(for_each)]
+pub fn z_y_axis_player(spritesheet: &mut Spritesheet, transform: &Transform) {
+    spritesheet.z_order = transform.position.y;
+    //println!("{}", spritesheet.z_order);
 }
 
+fn lerp(from: f32, to: f32, t: f32) -> f32 {
+    from + (to - from) * t
+}
+fn lerp_vec2(from: Vec2, to: Vec2, t: f32) -> Vec2 {
+    Vec2::new(lerp(from.x, to.x, t), lerp(from.y, to.y, t))
+}
+
+const SMOOTHING_FACTOR: f32 = 10.0;
 #[system]
-pub fn draw_fps(
-    #[resource] canvas: &mut WindowCanvas,
-    #[resource] texture_creator: &mut TextureCreator<WindowContext>,
-    #[resource] ttf_ctx: &Arc<Sdl2TtfContext>,
-    #[state] frames: &mut u32,
-    #[state] fps: &mut String,
-    #[state] last_time: &mut Instant,
+pub fn camera(
+    #[resource] time: &Time,
+    #[resource] camera: &mut Box<Camera2D>,
+    #[resource] track: &Track,
 ) {
-    let elapsed = last_time.elapsed();
-    //println!("{}", elapsed.as_micros());
-    if elapsed >= Duration::from_secs(1) {
-        *fps = frames.clone().to_string();
-        *frames = 0;
-        *last_time = Instant::now();
-    }
+    camera.target = lerp_vec2(
+        camera.target,
+        track.pos * METERS_TO_PIXELS,
+        time.delta * SMOOTHING_FACTOR,
+    );
+    camera.zoom = vec2(2.0 / screen_width(), 2.0 / screen_height());
+    set_camera(camera.as_ref());
+}
 
-    let font = ttf_ctx
-        .load_font("/usr/share/fonts/ubuntu/Ubuntu-M.ttf", 12)
-        .unwrap();
-    let surface = font
-        .render(format!("FPS: {}", fps).as_str())
-        .blended(Color::WHITE)
-        .unwrap();
-    let texture = texture_creator
-        .create_texture_from_surface(surface)
-        .unwrap();
-    let dst = Rect::new(0, 0, texture.query().width, texture.query().height);
-    canvas.copy(&texture, None, Some(dst)).unwrap();
-    *frames += 1;
+#[system]
+pub fn camera_ui() {
+    set_default_camera();
+}
+
+#[system(for_each)]
+pub fn track_player(#[resource] track: &mut Track, _: &Player, t: &Transform) {
+    track.pos = t.position;
+    println!("Player: {}", t.position);
 }
 
+// ---- RENDER SYSTEM ----
 const METERS_TO_PIXELS: f32 = 100.0; // 1 metro = 100 pixels
-fn calculate_dst(position:Vec2, size:Vec2, scale:Vec2) -> FRect {
+fn calculate_dst(position: Vec2, size: Vec2, scale: Vec2) -> Rect {
     // Corrigindo os cálculos de tamanho
     let sizex = size.x * scale.x * METERS_TO_PIXELS;
     let sizey = size.y * scale.y * METERS_TO_PIXELS;
@@ -63,104 +71,152 @@ fn calculate_dst(position:Vec2, size:Vec2, scale:Vec2) -> FRect {
     // Corrigindo os cálculos de posição
     let px = (position.x * METERS_TO_PIXELS) - (sizex / 2.0);
     let py = (position.y * METERS_TO_PIXELS) - (sizey / 2.0);
-    
-    FRect::new(px, py, sizex, sizey)
+
+    Rect::new(px, py, sizex, sizey)
+}
+
+trait Renderable {
+    fn z_order(&self) -> f32;
+    fn render(&self, transform: &Transform, textures: &HashMap<String, Arc<Texture2D>>);
+}
+
+impl Renderable for Sprite {
+    fn z_order(&self) -> f32 {
+        self.z_order
+    }
+
+    fn render(&self, transform: &Transform, textures: &HashMap<String, Arc<Texture2D>>) {
+        let texture = textures.get(self.image_path.as_str());
+        let texture = match texture {
+            Some(t) => t,
+            None => {
+                eprintln!("Erro textura");
+                return;
+            }
+        };
+        let dst = calculate_dst(
+            transform.position,
+            Vec2::new(texture.width() as f32, texture.height() as f32) / METERS_TO_PIXELS,
+            transform.scale,
+        );
+        draw_texture_ex(
+            &texture,
+            dst.x,
+            dst.y,
+            WHITE,
+            DrawTextureParams {
+                dest_size: Some(macroquad::math::Vec2::new(dst.w, dst.h)),
+                rotation: transform.rotation,
+                pivot: Some(macroquad::math::Vec2::new(dst.w / 2., dst.h / 2.)),
+                ..Default::default()
+            },
+        );
+    }
 }
+
+impl Renderable for (&Spritesheet, &AnimationPlayer) {
+    fn z_order(&self) -> f32 {
+        self.0.z_order
+    }
+
+    fn render(&self, transform: &Transform, textures: &HashMap<String, Arc<Texture2D>>) {
+        let texture = textures.get(self.0.image_path.as_str());
+        let texture = match texture {
+            Some(t) => t,
+            None => {
+                eprintln!("Erro textura");
+                return;
+            }
+        };
+
+        let rect = self
+            .0
+            .animations
+            .get(self.1.current_animation.as_str())
+            .expect("Animation not found")
+            .get(self.1.current_frame)
+            .expect("Animation frame out of bounds");
+        let rect = Rect::new(rect.x as f32, rect.y as f32, rect.w as f32, rect.z as f32);
+        let dst = calculate_dst(transform.position, self.0.dst_size, transform.scale);
+
+        draw_texture_ex(
+            &texture,
+            dst.x,
+            dst.y,
+            WHITE,
+            DrawTextureParams {
+                dest_size: Some(macroquad::math::Vec2::new(dst.w, dst.h)),
+                source: Some(rect),
+                rotation: transform.rotation,
+                pivot: Some(macroquad::math::Vec2::new(dst.w / 2., dst.h / 2.)),
+                ..Default::default()
+            },
+        );
+    }
+}
+
+impl Renderable for DebugSprite {
+    fn z_order(&self) -> f32 {
+        self.z_order
+    }
+
+    fn render(&self, transform: &Transform, textures: &HashMap<String, Arc<Texture2D>>) {
+        let dst = calculate_dst(transform.position, self.size, transform.scale);
+        draw_rectangle_ex(
+            dst.x,
+            dst.y,
+            dst.w,
+            dst.h,
+            DrawRectangleParams {
+                offset: Vec2::ZERO,
+                rotation: transform.rotation,
+                color: self.color,
+            },
+        );
+    }
+}
+
 #[system]
 #[read_component(Sprite)]
 #[read_component(Transform)]
 #[read_component(DebugSprite)]
 #[read_component(Spritesheet)]
 #[read_component(AnimationPlayer)]
-#[read_component(DynamicBody)]
-#[read_component(StaticBody)]
-pub fn render(
-    world: &mut SubWorld,
-    #[resource] canvas: &mut WindowCanvas,
-    #[resource] textures: &HashMap<String, Arc<Texture<'_>>>,
-) {
+pub fn render(world: &mut SubWorld, #[resource] textures: &HashMap<String, Arc<Texture2D>>) {
+    let mut renderables: Vec<(&Transform, &dyn Renderable)> = Vec::new();
+
+    //Registering
     let mut sprite_query = <(&Sprite, &Transform)>::query();
     for (sprite, transform) in sprite_query.iter(world) {
-        let texture = textures.get(sprite.image_path.as_str()).unwrap();
-        let texquery = texture.query();
-        let dst = calculate_dst(transform.position, Vec2::new(texquery.width as f32, texquery.height as f32), transform.scale);
-        canvas
-            .copy_ex_f(
-                texture.as_ref(),
-                None,
-                dst,
-                transform.rotation.into(),
-                None,
-                false,
-                false,
-            )
-            .unwrap();
+        renderables.push((transform, sprite));
     }
 
-    // let mut debug_query = <(&DebugSprite, &Transform)>::query();
-    // for (sprite, transform) in debug_query.iter_mut(world) {
-    //     let sizex = sprite.size.x * transform.scale.x * METERS_TO_PIXELS;
-    //     let sizey = sprite.size.y * transform.scale.y * METERS_TO_PIXELS;
-    //     let px = transform.position.x * METERS_TO_PIXELS - (sizex / 2.0);
-    //     let py = transform.position.y * METERS_TO_PIXELS - (sizey / 2.0);
-    //     canvas.set_draw_color(sprite.color);
-    //     canvas.draw_frect(FRect::new(px, py, sizex, sizey)).unwrap();
-    // }
-
-    let mut anim_query = <(&Transform, &Spritesheet, &AnimationPlayer)>::query();
-    for (transform, spritesheet, player) in anim_query.iter_mut(world) {
-        let tex = textures.get(spritesheet.image_path.as_str());
-        let tex = tex.unwrap();
-
-        let rect = spritesheet
-            .animations
-            .get(player.current_animation.as_str())
-            .expect(
-                format!(
-                    "The animation '{}' does not exist in spritesheet.",
-                    player.current_animation,
-                )
-                .as_str(),
-            )
-            .get(player.current_frame)
-            .expect(
-                format!(
-                    "The position {} in animation {} is out of bounds.",
-                    player.current_frame, player.current_animation
-                )
-                .as_str(),
-            );
-        // rect width and height are already in pixels format, so we need to revert to meters before passing it to calculate_dst
-        let dst = calculate_dst(transform.position, spritesheet.dst_size, transform.scale);
-
-        canvas
-            .copy_ex_f(
-                tex.as_ref(),
-                Some(Rect::new(rect.x, rect.y, rect.w as u32, rect.z as u32)),
-                dst,
-                transform.rotation.into(),
-                None,
-                false,
-                false,
-            )
-            .unwrap();
+    let mut animated_storage: Vec<(&Transform, (&Spritesheet, &AnimationPlayer))> = Vec::new();
+    let mut animated_query = <(&Transform, &Spritesheet, &AnimationPlayer)>::query();
+    for (t, s, p) in animated_query.iter(world) {
+        animated_storage.push((t, (s, p)));
     }
+    animated_storage
+        .iter()
+        .for_each(|(t, c)| renderables.push((t, c)));
 
-    // DEBUG
-    let mut phys_query = <(&Transform, &DynamicBody)>::query();
-    for (transform, body) in phys_query.iter(world) {
-        canvas.set_draw_color(Color::CYAN);
-        canvas.draw_frect(calculate_dst(transform.position, Vec2::new(body.size.x, body.size.y), transform.scale)).unwrap();
+    let mut debug_query = <(&Transform, &DebugSprite)>::query();
+    for (transform, sprite) in debug_query.iter(world) {
+        renderables.push((transform, sprite));
     }
 
-    let mut phys_query = <(&Transform, &StaticBody)>::query();
-    for (transform, body) in phys_query.iter(world) {
-        canvas.set_draw_color(Color::MAGENTA);
-        canvas.draw_frect(calculate_dst(transform.position, Vec2::new(body.size.x, body.size.y), transform.scale)).unwrap();
-    }
-} 
+    renderables.sort_by(|a, b| {
+        let (_, x) = a;
+        let az = x.z_order();
+        let (_, x) = b;
+        let bz = x.z_order();
 
-#[system]
-pub fn present(#[resource] canvas: &mut WindowCanvas) {
-    canvas.present();
+        az.total_cmp(&bz)
+    });
+
+    //Rendering
+    for renderable in renderables.iter() {
+        let (transform, comp) = renderable;
+        comp.render(&transform, &textures);
+    }
 }
diff --git a/src/sys/tick.rs b/src/sys/tick.rs
index c6d8b3d..7f13b46 100644
--- a/src/sys/tick.rs
+++ b/src/sys/tick.rs
@@ -1,31 +1,40 @@
-use std::time::Instant;
-
-use glam::Vec2;
 use legion::*;
-use nalgebra::Vector2;
+use macroquad::{
+    color,
+    input::{is_key_down, is_mouse_button_down},
+    math::Vec2,
+    text::{draw_text, get_text_center},
+};
 
 use crate::{
-    comps::{AnimationPlayer, DynamicBody, Player, Spritesheet, Transform},
+    comps::{AnimationPlayer, Player, Spritesheet, Transform},
     game::Time,
-    input::InputContext,
-    physics::PhysicsContext,
+    input::{InputContext, RawAction},
 };
 
-#[system]
-pub fn time_update(#[resource] time: &mut Time) {
-    let now = Instant::now();
-    time.delta = now.duration_since(time.last).as_secs_f32();
-    time.last = now;
-}
-
 #[system]
 pub fn input_update(#[resource] input: &mut InputContext) {
     input.update();
+
+    for (k, a) in input.setup.keybindings.clone() {
+        match k {
+            RawAction::Key(k) => {
+                if is_key_down(k) {
+                    input.actions.push_front(a);
+                }
+            }
+            RawAction::MouseButton(b) => {
+                if is_mouse_button_down(b) {
+                    input.actions.push_front(a);
+                }
+            }
+        }
+    }
 }
 
 #[system(for_each)]
 pub fn step_animation(
-    #[resource] time: &Time,
+    #[resource] time: &mut Time,
     #[state] sprite_time: &mut f32,
     player: &mut AnimationPlayer,
     sheet: &Spritesheet,
@@ -52,58 +61,31 @@ pub fn step_animation(
     *sprite_time += time.delta;
 }
 
-#[system]
-pub fn step_physics(#[resource] physics: &mut PhysicsContext) {
-    physics.pipeline.step(
-        &physics.gravity,
-        &physics.integration_params,
-        &mut physics.islands,
-        &mut physics.broad_phase,
-        &mut physics.narrow_phase,
-        &mut physics.bodies,
-        &mut physics.colliders,
-        &mut physics.impulse_joints,
-        &mut physics.multibody_joints,
-        &mut physics.ccd_solver,
-        None,
-        &mut physics.hooks,
-        &mut physics.events,
-    );
-}
-
 #[system(for_each)]
-pub fn physics_integration(
-    #[resource] physics: &mut PhysicsContext,
+pub fn move_player(
+    #[resource] input_ctx: &mut InputContext,
+    #[resource] time: &Time,
     transform: &mut Transform,
-    body: &DynamicBody,
+    player: &Player,
 ) {
-    if let Some(b) = physics.bodies.get(body.handle.unwrap()) {
-        let t = b.translation();
-        let r = b.rotation();
-        transform.position = Vec2::new(t.x, t.y);
-        transform.rotation = r.angle();
-    }
+    let dir = input_ctx.move_direction;
+    transform.position += dir * player.speed * time.delta;
 }
 
 #[system(for_each)]
-pub fn move_player(
-    #[resource] input_ctx: &InputContext,
-    //#[resource] time: &Time,
-    #[resource] physics: &mut PhysicsContext,
-    //transform: &mut Transform,
+pub fn animate_player(
+    #[resource] input_ctx: &mut InputContext,
+    transform: &mut Transform,
     anim_player: &mut AnimationPlayer,
-    body: &mut DynamicBody,
-    player: &Player,
 ) {
-    let dir = input_ctx.move_direction;
-
-    // --- Atualiza a velocidade do corpo no mundo físico ---
-    if let Some(rigid_body) = physics.bodies.get_mut(body.handle.unwrap()) {
-        let velocity = Vector2::new(dir.x * player.speed, dir.y * player.speed);
-        if velocity.magnitude() != 0.0 {
-            rigid_body.set_linvel(velocity, true);
-        }
-    }
+    let txt_center = get_text_center("123", None, 24, 1., 0.);
+    draw_text(
+        format!("123").as_str(),
+        transform.position.x - txt_center.x,
+        transform.position.y - txt_center.y,
+        24.,
+        color::GREEN,
+    );
     match input_ctx.move_direction {
         Vec2 { x: 1.0, .. } => {
             anim_player.current_animation = "right".to_string();
diff --git a/src/window.rs b/src/window.rs
deleted file mode 100644
index 1ea35f2..0000000
--- a/src/window.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use sdl2::{
-    EventPump,
-    render::{TextureCreator, WindowCanvas},
-    video::WindowContext,
-};
-
-pub fn init_sdl<'a>() -> Result<(WindowCanvas, TextureCreator<WindowContext>, EventPump), String> {
-    let sdl_ctx = sdl2::init()?;
-
-    let video = sdl_ctx.video()?;
-    let event_pump = sdl_ctx.event_pump()?;
-
-    let window = video
-        .window("Aquatunnel", 800, 600)
-        .position_centered()
-        .build()
-        .unwrap();
-
-    let canvas = window
-        .clone()
-        .into_canvas()
-        .present_vsync()
-        //.accelerated()
-        .build()
-        .unwrap();
-
-    let texture_creator = canvas.texture_creator();
-    Ok((canvas, texture_creator, event_pump))
-}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 6**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>src/sys/tick.rs (1)</summary><blockquote>

`90-113`: _🛠️ Refactor suggestion_

**Exact vector pattern matching misses diagonal movement**

`Vec2 { x: 1.0, .. }` only matches a _perfect_ unit vector along +X; any diagonal (0.707, 0.707) fails.  
Use thresholds instead:

```rust
let dir = input_ctx.move_direction;

if dir.x > 0.0 {
    anim_player.current_animation = "right".into();
} else if dir.x < 0.0 {
    anim_player.current_animation = "left".into();
} else if dir.y > 0.0 {
    anim_player.current_animation = "down".into();
} else if dir.y < 0.0 {
    anim_player.current_animation = "up".into();
} else {
    anim_player.playing = false;
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (16)</summary><blockquote>

<details>
<summary>src/load.rs (1)</summary><blockquote>

`38-38`: **Consider using a logger instead of println**

Using `println!` for logging is fine for development, but consider using a proper logging framework like `log` with `env_logger` for better control over log levels in production.

```diff
- println!("imagem {} carregada.", path);
+ log::info!("imagem {} carregada.", path);
```

</blockquote></details>
<details>
<summary>src/comps.rs (3)</summary><blockquote>

`29-34`: **Consider adding a `Default` impl (or builder) for `Sprite` to avoid boiler-plate when spawning many entities**  
Most call-sites will likely initialise a `Sprite` with the same “neutral” depth (e.g. `0.0`).  Supplying a `Default` implementation (or even a tiny builder helper) keeps the call-sites terse and shields them from future field additions.

```diff
 impl Default for Sprite {
     fn default() -> Self {
-        Self { image_path: String::new(), z_order: 0.0 }
+        Self {
+            image_path: String::new(),
+            z_order: 0.0,
+        }
     }
 }
```

---

`35-41`: **Derive `Copy` for very small value types to avoid superfluous heap moves**  
`DebugSprite` (and `Sprite` / `Transform`) are < 24 bytes and immutable in practice.  
Deriving `Copy` allows systems to access these components without cloning or moving them out of the ECS storage, which micro-optimises inner-loop performance.

```diff
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq)]
 pub struct DebugSprite { … }
```
(Do the same for `Sprite`, `Transform`, and `Player` if no interior mutability is introduced.)

---

`50-56`: **Provide a `Default` implementation for `AnimationPlayer` to simplify entity construction**  
Many entities will share identical starting state (`playing: true`, `current_frame: 0`, etc.). Adding:

```rust
impl Default for AnimationPlayer {
    fn default() -> Self {
        Self {
            current_animation: String::new(),
            current_frame: 0,
            playing: false,
            frame_duration: 0.1,
        }
    }
}
```

lets call-sites write `AnimationPlayer { ..Default::default() }` and shields them from future field additions.

</blockquote></details>
<details>
<summary>src/entitites.rs (3)</summary><blockquote>

`22-23`: **Hard-coded z-orders – extract semantic constants to avoid magic numbers**  
Using naked `2.` leaks render-ordering details into gameplay code. A descriptive constant such as `PLAYER_Z` (in a dedicated `constants.rs`) improves readability and helps keep ordering consistent.

---

`30-30`: **Prefer a named constant for player speed**  
`0.7` gives no hint of its unit (tiles/ s? px/ frame?). Declaring `const PLAYER_SPEED: f32 = 0.7;` makes intent explicit and eases tuning.

---

`31-35`: **Remove commented-out code to keep the repository clean**  
Source control already tracks history; leaving dead code invites bit-rot and misleads future readers. If you still need an optional collider/visualiser, gate it behind a feature flag instead.

</blockquote></details>
<details>
<summary>src/game.rs (3)</summary><blockquote>

`24-32`: **`Track` could be merged into `Transform` or replaced by an existing component**  
If its sole purpose is to expose the player’s position to the camera system, consider:

1. Querying the player entity’s `Transform` directly in `camera_system`, or  
2. Tagging the player with a `CameraTarget` marker component.

This removes duplicated state and guarantees the camera always follows the authoritative position.

---

`69-73`: **Repeated `Time` insertion each frame might allocate – reuse the resource**  
Creating a fresh struct is fine, but consider `resources.insert(Time { delta })` only once and mutating it inside the loop to avoid per-frame hash-map churn (Legion’s `get_mut` + assignment).

---

`32-85`: **`run_game` never returns `Err`, so the `Result` wrapper is redundant**  
Unless future code paths propagate an error, change the signature to `async fn run_game() { … }` and drop the final `Ok(())`.

</blockquote></details>
<details>
<summary>src/input.rs (2)</summary><blockquote>

`26-45`: **Repeated function dispatch can be simplified**

The two almost-identical `match` blocks make maintenance harder.  
Consider storing the input methods in an array / slice of function pointers and indexing it, e.g.:

```rust
const MOVE_FUNCS: [fn(&InputContext) -> Vec2; 6] = [
    InputContext::method0,
    InputContext::method1,
    /* … */
];

self.move_direction = MOVE_FUNCS[self.setup.move_method as usize](self);
self.look_direction = MOVE_FUNCS[self.setup.look_method as usize](self);
```
This eliminates duplicate code and bounds-checks automatically.

---

`82-88`: **Unimplemented joystick branches crash at runtime**

`todo!()` panics the first time a player selects joystick controls.  
Either gate these options off, or return `Vec2::ZERO` until they are implemented.

```diff
-        todo!()
+        // TODO: implement proper joystick polling
+        Vec2::ZERO
```

</blockquote></details>
<details>
<summary>src/sys/tick.rs (3)</summary><blockquote>

`37-62`: **`Time` does not need to be mutable**

`step_animation` only reads from `time`; declare the parameter as `&Time` to make the intent explicit and allow more parallelism in Legion.

```diff
-    #[resource] time: &mut Time,
+    #[resource] time: &Time,
```

---

`81-88`: **Hard-coded debug text pollutes the render output**

`"123"` is rendered every frame at the player position.  
Wrap this behind a debug flag or remove it before shipping.

```diff
-    let txt_center = get_text_center("123", None, 24, 1., 0.);
-    draw_text(
-        format!("123").as_str(),
-        transform.position.x - txt_center.x,
-        transform.position.y - txt_center.y,
-        24.,
-        color::GREEN,
-    );
```

---

`59-62`: **Remove noisy `println!` in hot path**

`track_player` logs the player position each frame; this floods stdout and hurts performance in release builds.  
Guard it behind `cfg!(debug_assertions)` or a proper logging level.

</blockquote></details>
<details>
<summary>src/sys/render.rs (1)</summary><blockquote>

`185-222`: **Trait-object allocation in tight loop**

`renderables: Vec<(&Transform, &dyn Renderable)>` allocates a new `Vec` and repeatedly pushes trait objects every frame.  
Two quick wins:

1. Re-use the vector with `clear()` to avoid allocation churn (`resources.insert(Vec::<…>::with_capacity(N))`).
2. Store an enum (`enum RenderRef { Sprite(&Sprite), Anim(&Spritesheet,&AnimationPlayer), Debug(&DebugSprite) }`) instead of `&dyn Renderable` to avoid dynamic dispatch at render time.

These changes can significantly lower per-frame CPU usage on mid-tier hardware.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 428dcc7cf6711a5457d494b362a08e3bcd179df9 and e4686c4d874c0ddabb6cfa0198410dcc95f96b1c.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `Cargo.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (13)</summary>

* `Cargo.toml` (1 hunks)
* `src/comps.rs` (2 hunks)
* `src/entitites.rs` (2 hunks)
* `src/game.rs` (1 hunks)
* `src/input.rs` (1 hunks)
* `src/load.rs` (1 hunks)
* `src/main.rs` (1 hunks)
* `src/physics.rs` (0 hunks)
* `src/sys/load.rs` (0 hunks)
* `src/sys/mod.rs` (1 hunks)
* `src/sys/render.rs` (1 hunks)
* `src/sys/tick.rs` (2 hunks)
* `src/window.rs` (0 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (3)</summary>

* src/window.rs
* src/physics.rs
* src/sys/load.rs

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>src/main.rs (2)</summary><blockquote>

<details>
<summary>src/load.rs (1)</summary>

* `load` (9-40)

</details>
<details>
<summary>src/game.rs (1)</summary>

* `run_game` (32-85)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (10)</summary><blockquote>

<details>
<summary>Cargo.toml (1)</summary>

`8-10`: **Good additions of futures and macroquad dependencies**

The additions of `futures` for async handling and `macroquad` as the new rendering engine align perfectly with the PR objective of migrating to Macroquad.

</details>
<details>
<summary>src/sys/mod.rs (1)</summary>

`1-2`: **Update reflects architectural changes**

The removal of the `load` module from public exports aligns with the architectural shift to using Macroquad's async loading functionality instead of SDL2-based loading systems.

</details>
<details>
<summary>src/main.rs (2)</summary>

`5-5`: **Appropriate module addition**

Adding the new `load` module import properly reflects the architectural changes in the codebase.

---

`8-10`: **Good conversion to async for Macroquad integration**

The conversion of the main function to an async function with the Macroquad attribute and proper await on the game loop is correctly implemented. This is required for Macroquad's async asset loading and game loop.

</details>
<details>
<summary>src/load.rs (3)</summary>

`1-8`: **Well-structured imports**

The imports are well organized with standard library imports first, followed by external crates, and then internal modules. This follows good Rust code organization practices.

---

`9-26`: **Good implementation of texture caching**

The texture loading system efficiently checks for already loaded textures to avoid redundant loading operations. The implementation properly uses the ECS pattern to query for all entities that need textures.

---

`28-34`: **Excellent use of async concurrency for texture loading**

The implementation uses `futures::join_all` to load multiple textures concurrently, which is a performance optimization that takes advantage of Macroquad's async texture loading capabilities.

</details>
<details>
<summary>src/entitites.rs (2)</summary>

`1-1`: **File is miss-spelled (`entitites.rs`) – consider renaming to `entities.rs` for clarity**  
Typos in module names propagate to `mod` statements elsewhere and make the codebase harder to navigate.

---

`45-48`: **Negative `z_order` for ground: verify sort algorithm handles it correctly**  
If the renderer sorts ascending, the ground will draw before the background (potentially hiding it). Confirm the sort direction in `render::render_system` and adjust the sign convention if necessary.

</details>
<details>
<summary>src/sys/render.rs (1)</summary>

`139-140`: **Verify sprite-sheet field order**

`Rect::new(rect.x, rect.y, rect.w, rect.z)` assumes `rect.w` == width and `rect.z` == height.  
If the original tuple is `(x, y, w, h)` you want `rect.z` **→** height; if it is `(x, y, h, w)` the parameters are flipped, producing stretched frames.

Please double-check the underlying data or add a unit test that renders a known frame.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-30T21:00:09Z

Review comments:
diff_hunk: @@ -5,9 +5,8 @@ edition = "2024"
 
 [dependencies]
 anyhow = "1.0.97"
-glam = "0.30.1"
+futures = "0.3.31"
 legion = "0.4.0"
-miniquad = "0.4.8"
+macroquad = "0.4.14"
 nalgebra = "0.33.2"
 rapier2d = "0.24.0"
path: Cargo.toml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider removing the rapier2d dependency**

The rapier2d dependency remains in the Cargo.toml, but according to the PR summary, physics-related systems have been eliminated. If you're no longer using rapier2d anywhere in the codebase, you should remove this unused dependency.

```diff
 anyhow = "1.0.97"
 futures = "0.3.31"
 legion = "0.4.0"
 macroquad = "0.4.14"
 nalgebra = "0.33.2"
-rapier2d = "0.24.0"
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
anyhow = "1.0.97"
futures = "0.3.31"
legion = "0.4.0"
macroquad = "0.4.14"
nalgebra = "0.33.2"
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-30T21:00:07Z
author_association: NONE
diff_hunk: @@ -0,0 +1,40 @@
+use std::{collections::HashMap, sync::Arc};
+
+use futures::future::join_all;
+use legion::{Resources, World, query::*};
+use macroquad::texture::{Texture2D, load_texture};
+
+use crate::comps::{Sprite, Spritesheet};
+
+pub async fn load(world: &mut World, resources: &mut Resources) {
+    let mut textures = resources
+        .get_mut::<HashMap<String, Arc<Texture2D>>>()
+        .unwrap();
+
+    let mut img_paths: Vec<String> = Vec::new();
+    let mut query = <&Sprite>::query();
+    for spr in query.iter(world) {
+        if !textures.contains_key(&spr.image_path) {
+            img_paths.push(spr.image_path.clone());
+        }
+    }
+    let mut query = <&Spritesheet>::query();
+    for spr in query.iter(world) {
+        if !textures.contains_key(&spr.image_path) {
+            img_paths.push(spr.image_path.clone());
+        }
+    }
+
+    let futures = img_paths.iter().map(|path| async move {
+        let tex = load_texture(path).await.unwrap();
+        (path.clone(), Arc::new(tex))
path: src/load.rs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add error handling for texture loading**

The current implementation uses `.unwrap()` which will panic if a texture fails to load. In a production environment, you should implement proper error handling.

```diff
 let futures = img_paths.iter().map(|path| async move {
-    let tex = load_texture(path).await.unwrap();
+    match load_texture(path).await {
+        Ok(tex) => Some((path.clone(), Arc::new(tex))),
+        Err(err) => {
+            eprintln!("Failed to load texture {}: {}", path, err);
+            None
+        }
+    }
 });

-let loaded_texs: Vec<(String, Arc<Texture2D>)> = join_all(futures).await;
+let loaded_texs: Vec<Option<(String, Arc<Texture2D>)>> = join_all(futures).await;

 for (path, tex) in loaded_texs {
+    if let Some((path, tex)) = tex_result {
         tex.set_filter(macroquad::texture::FilterMode::Nearest);
         textures.insert(path.clone(), tex);
         println!("imagem {} carregada.", path);
+    }
 }
```


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-30T21:00:08Z
author_association: NONE
diff_hunk: @@ -1,101 +1,85 @@
-use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc, time::Instant};
+use std::{collections::HashMap, sync::Arc};
 
-use legion::{Resources, Schedule, World, systems::CommandBuffer};
-use sdl2::{EventPump, event::Event, keyboard::Keycode, render::Texture};
+use legion::{Entity, Resources, Schedule, World, systems::CommandBuffer};
+use macroquad::{
+    camera::Camera2D,
+    input::{KeyCode, is_key_down},
+    math::Vec2,
+    texture::Texture2D,
+    time::get_frame_time,
+    window::next_frame,
+};
 
 use crate::{
+    comps::Transform,
     entitites::populate,
     input::{InputContext, InputSetup},
-    physics::PhysicsContext,
-    window::init_sdl,
+    load::load,
     sys::{
-        load::{load_physics_system, load_spritesheet_system, load_system},
-        render, tick,
+        render::{self, camera_system, camera_ui_system},
+        tick,
     },
 };
 
 pub struct Time {
-    pub last: Instant,
     pub delta: f32,
 }
 
-pub fn run_game() -> Result<(), String> {
-    let (canvas, texture_creator, event_pump) = init_sdl()?;
-    let event_pump = Rc::new(RefCell::new(event_pump));
-    let textures: HashMap<String, Arc<Texture<'_>>> = HashMap::new();
+pub struct Track {
+    pub pos: Vec2,
+}
+
+pub async fn run_game() -> Result<(), String> {
+    let textures: HashMap<String, Arc<Texture2D>> = HashMap::new();
     let mut world = World::default();
     let mut resources = Resources::default();
-
-    let ttf_ctx = Arc::new(sdl2::ttf::init().unwrap());
-    let time = Time {
-        last: Instant::now(),
-        delta: 0.0,
-    };
-
-    let input_ctx = InputContext::new(event_pump.clone(), InputSetup::default());
-    let physics_ctx = PhysicsContext::default();
-
+    let camera = Box::new(Camera2D::default());
+    let input_ctx = InputContext::new(InputSetup::default());
+    let track: Track = Track { pos: Vec2::ZERO };
+    resources.insert(track);
     resources.insert(textures);
-    resources.insert(canvas);
-    resources.insert(texture_creator);
     resources.insert(input_ctx);
-    resources.insert(physics_ctx);
-    resources.insert(ttf_ctx);
-    resources.insert(time);
+    resources.insert(camera);
     resources.insert(CommandBuffer::new(&world));
path: src/game.rs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Avoid boxing `Camera2D`; store it directly in `Resources`**  
`Camera2D` is a small `Copy` struct – boxing adds an unnecessary heap allocation and an extra level of indirection.

```diff
-    let camera = Box::new(Camera2D::default());
+    let camera = Camera2D::default();
 …
-    resources.insert(camera);
+    resources.insert(camera);
```
This also simplifies retrieval: `resources.get_mut::<Camera2D>()` instead of working with `Box<…>`.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    let camera = Camera2D::default();
    let input_ctx = InputContext::new(InputSetup::default());
    let track: Track = Track { pos: Vec2::ZERO };
    resources.insert(track);
    resources.insert(textures);
    resources.insert(input_ctx);
    resources.insert(camera);
    resources.insert(CommandBuffer::new(&world));
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-30T21:00:08Z
author_association: NONE
diff_hunk: @@ -1,89 +1,119 @@
-use std::{
-    cell::RefCell,
-    collections::{HashMap, VecDeque},
-    rc::Rc,
-};
+use std::collections::{HashMap, VecDeque};
 
-use glam::Vec2;
-use sdl2::{EventPump, event::Event, keyboard::Keycode};
+use macroquad::input::{KeyCode, MouseButton, is_key_down};
+use macroquad::input::{mouse_delta_position, mouse_position};
+use macroquad::math::Vec2;
+use macroquad::window::screen_width;
path: src/input.rs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Import both `screen_width` _and_ `screen_height`**

`method1` relies on both dimensions but only `screen_width` is imported.  
Add `screen_height` to prevent a compilation error after the fix proposed below.

```diff
-use macroquad::window::screen_width;
+use macroquad::window::{screen_width, screen_height};
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
use macroquad::input::{KeyCode, MouseButton, is_key_down};
use macroquad::input::{mouse_delta_position, mouse_position};
use macroquad::math::Vec2;
- use macroquad::window::screen_width;
+ use macroquad::window::{screen_width, screen_height};
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-30T21:00:08Z
author_association: NONE
diff_hunk: @@ -1,89 +1,119 @@
-use std::{
-    cell::RefCell,
-    collections::{HashMap, VecDeque},
-    rc::Rc,
-};
+use std::collections::{HashMap, VecDeque};
 
-use glam::Vec2;
-use sdl2::{EventPump, event::Event, keyboard::Keycode};
+use macroquad::input::{KeyCode, MouseButton, is_key_down};
+use macroquad::input::{mouse_delta_position, mouse_position};
+use macroquad::math::Vec2;
+use macroquad::window::screen_width;
 
 pub struct InputContext {
     pub move_direction: Vec2,
     pub look_direction: Vec2,
     pub actions: VecDeque<InputAction>,
-    setup: InputSetup,
-    event_pump: Rc<RefCell<EventPump>>,
+    pub setup: InputSetup,
 }
 
 impl InputContext {
-    pub fn new(event_pump: Rc<RefCell<EventPump>>, setup: InputSetup) -> Self {
+    pub fn new(setup: InputSetup) -> Self {
         InputContext {
             move_direction: Vec2::ZERO,
             look_direction: Vec2::ZERO,
             actions: VecDeque::new(),
             setup,
-            event_pump,
         }
     }
 
     pub fn update(&mut self) {
-        // Essa função deve ler inputs brutos e atualizar as informações move_direction,
-        // look_direction e actions, para serem exportadas à lógica do jogo.
-
-        for event in self.event_pump.borrow_mut().poll_iter() {
-            match event {
-                Event::KeyDown {
-                    keycode: Some(k), ..
-                } => {
-                    if let Some(action) = self.setup.keybindings.get(&RawAction::Key(k)) {
-                        self.actions.push_back(action.clone());
-                    }
-                }
-
-                _ => {}
-            }
-        }
+        self.move_direction = match self.setup.move_method {
+            0 => self.method0(),
+            1 => self.method1(),
+            2 => self.method2(),
+            3 => self.method3(),
+            4 => self.method4(),
+            5 => self.method5(),
+            _ => Vec2::ZERO,
+        };
 
-        match self.setup.move_method {
-            0 => self.move_direction = self.method_0(),
-            _ => {}
-        }
+        self.look_direction = match self.setup.look_method {
+            0 => self.method0(),
+            1 => self.method1(),
+            2 => self.method2(),
+            3 => self.method3(),
+            4 => self.method4(),
+            5 => self.method5(),
+            _ => Vec2::ZERO,
+        };
     }
 
-    fn method_0(&self) -> Vec2 {
-        let event_pump_ref = self.event_pump.borrow_mut();
-        let keyboard_state = event_pump_ref.keyboard_state();
+    // WASD
+    fn method0(&self) -> Vec2 {
         // -A +D
-        let x_signal = -(keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::A) as i32)
-            + (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::D) as i32);
+        let x_signal = -(is_key_down(KeyCode::A) as i32) + (is_key_down(KeyCode::D) as i32);
         // -W +S
-        let y_signal = -(keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::W) as i32)
-            + (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::S) as i32);
+        let y_signal = -(is_key_down(KeyCode::W) as i32) + (is_key_down(KeyCode::S) as i32);
 
         Vec2::new(x_signal as f32, y_signal as f32).normalize_or_zero()
     }
+
+    // Direção do centro da tela pro mouse
+    fn method1(&self) -> Vec2 {
+        let (mouse_x, mouse_y) = mouse_position();
+        let center_x = screen_width() / 2.0;
+        let center_y = screen_width() / 2.0;
+
+        let direction = Vec2::new(mouse_x - center_x, mouse_y - center_y);
+        direction.normalize_or_zero()
+    }
path: src/input.rs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Incorrect Y-axis centre calculation**

`center_y` currently uses `screen_width()` instead of `screen_height()`, which skews the look vector on non-square windows.

```diff
-        let center_y = screen_width() / 2.0;
+        let center_y = screen_height() / 2.0;
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
        let (mouse_x, mouse_y) = mouse_position();
        let center_x = screen_width() / 2.0;
        let center_y = screen_height() / 2.0;

        let direction = Vec2::new(mouse_x - center_x, mouse_y - center_y);
        direction.normalize_or_zero()
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-30T21:00:08Z
author_association: NONE
diff_hunk: @@ -1,31 +1,40 @@
-use std::time::Instant;
-
-use glam::Vec2;
 use legion::*;
-use nalgebra::Vector2;
+use macroquad::{
+    color,
+    input::{is_key_down, is_mouse_button_down},
+    math::Vec2,
+    text::{draw_text, get_text_center},
+};
 
 use crate::{
-    comps::{AnimationPlayer, DynamicBody, Player, Spritesheet, Transform},
+    comps::{AnimationPlayer, Player, Spritesheet, Transform},
     game::Time,
-    input::InputContext,
-    physics::PhysicsContext,
+    input::{InputContext, RawAction},
 };
 
-#[system]
-pub fn time_update(#[resource] time: &mut Time) {
-    let now = Instant::now();
-    time.delta = now.duration_since(time.last).as_secs_f32();
-    time.last = now;
-}
-
 #[system]
 pub fn input_update(#[resource] input: &mut InputContext) {
     input.update();
+
+    for (k, a) in input.setup.keybindings.clone() {
+        match k {
+            RawAction::Key(k) => {
+                if is_key_down(k) {
+                    input.actions.push_front(a);
+                }
+            }
+            RawAction::MouseButton(b) => {
+                if is_mouse_button_down(b) {
+                    input.actions.push_front(a);
+                }
+            }
+        }
+    }
path: src/sys/tick.rs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Avoid per-frame cloning of the keybinding map**

`clone()` reallocates every frame.  
A borrowing pattern keeps the hot path allocation-free:

```rust
for (raw, action) in input.setup.keybindings.iter() {
    let pressed = match raw {
        RawAction::Key(k) => is_key_down(*k),
        RawAction::MouseButton(b) => is_mouse_button_down(*b),
    };
    if pressed {
        input.actions.push_back(*action);
    }
}
```
(collect into a small `Vec<InputAction>` first if the borrow checker complains)

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-30T21:00:08Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-30T20:56:54Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->

> [!NOTE]
> Currently processing new changes in this PR. This may take a few minutes, please wait...
> 
> <details>
> <summary>📥 Commits</summary>
> 
> Reviewing files that changed from the base of the PR and between 428dcc7cf6711a5457d494b362a08e3bcd179df9 and e4686c4d874c0ddabb6cfa0198410dcc95f96b1c.
> 
> </details>
> 
> <details>
> <summary>⛔ Files ignored due to path filters (1)</summary>
> 
> * `Cargo.lock` is excluded by `!**/*.lock`
> 
> </details>
> 
> <details>
> <summary>📒 Files selected for processing (13)</summary>
> 
> * `Cargo.toml` (1 hunks)
> * `src/comps.rs` (2 hunks)
> * `src/entitites.rs` (2 hunks)
> * `src/game.rs` (1 hunks)
> * `src/input.rs` (1 hunks)
> * `src/load.rs` (1 hunks)
> * `src/main.rs` (1 hunks)
> * `src/physics.rs` (0 hunks)
> * `src/sys/load.rs` (0 hunks)
> * `src/sys/mod.rs` (1 hunks)
> * `src/sys/render.rs` (1 hunks)
> * `src/sys/tick.rs` (2 hunks)
> * `src/window.rs` (0 hunks)
> 
> </details>
> 
> ```ascii
>  ________________________________________________________________________________________________________________________________
> < Test state coverage, not code coverage. Identify and test significant program states. Just testing lines of code isn't enough. >
>  --------------------------------------------------------------------------------------------------------------------------------
>   \
>    \   (\__/)
>        (•ㅅ•)
>        / 　 づ
> ```

<!-- end of auto-generated comment: review in progress by coderabbit.ai -->
<!-- usage_tips_start -->

> [!TIP]
> <details>
> <summary>You can disable poems in the walkthrough.</summary>
> 
> Disable the `reviews.poem` setting in your project's settings in CodeRabbit to disable the poems in the walkthrough.
> 
> </details>

<!-- usage_tips_end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Docstrings were successfully generated. (🔄  Check again to generate docstrings again)

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=CoininDev/aquatunnel&utm_content=1):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-04-30T21:00:37Z
body: > [!NOTE]
> Generated docstrings for this pull request at https://github.com/CoininDev/aquatunnel/pull/2
author_association: NONE