1use std::io;
2
3use bevy::app::AppExit;
4use bevy::diagnostic::FrameTimeDiagnosticsPlugin;
5use bevy::input::keyboard::KeyboardInput;
6use bevy::input::ButtonState;
7use bevy::prelude::*;
8use bevy::utils::error;
9use bevy_ratatui::event::KeyEvent;
10use bevy_ratatui::terminal::RatatuiContext;
11use bevy_ratatui::RatatuiPlugins;
12use bevy_ratatui_render::{RatatuiRenderContext, RatatuiRenderPlugin};
13use crossterm::event::{KeyCode, KeyEventKind, KeyEventState, KeyModifiers};
14
15fn main() {
20 App::new()
21 .add_plugins((
22 DefaultPlugins,
23 FrameTimeDiagnosticsPlugin,
25 RatatuiPlugins::default(),
26 RatatuiRenderPlugin::new("main", (256, 256)).disable(),
27 ))
28 .insert_resource(ClearColor(Color::BLACK))
29 .add_systems(Startup, setup_camera_system)
30 .add_systems(Startup, setup_scene_system)
31 .add_systems(Update, draw_scene_system.map(error))
32 .add_systems(
33 Update,
34 passthrough_keyboard_events_system.before(handle_input_system),
35 )
36 .add_systems(Update, handle_input_system)
37 .run();
38}
39
40fn setup_camera_system(mut commands: Commands, ratatui_render: Res<RatatuiRenderContext>) {
42 commands.spawn((
43 Camera3d::default(),
44 Camera {
45 target: ratatui_render.target("main").unwrap_or_default(),
46 ..default()
47 },
48 Transform::from_xyz(3., 3., 3.).looking_at(Vec3::ZERO, Vec3::Z),
49 ));
50}
51
52fn draw_scene_system(
54 mut ratatui: ResMut<RatatuiContext>,
55 rat_render: Res<RatatuiRenderContext>,
56) -> io::Result<()> {
57 ratatui.draw(|frame| {
58 if let Some(widget) = rat_render.widget("main") {
59 frame.render_widget(widget, frame.area());
60 }
61 })?;
62
63 Ok(())
64}
65
66fn passthrough_keyboard_events_system(
68 mut read_keyboard: EventReader<KeyboardInput>,
69 mut write_crossterm: EventWriter<KeyEvent>,
70) {
71 for ev in read_keyboard.read() {
72 write_crossterm.send(KeyEvent(crossterm::event::KeyEvent {
73 code: match ev.key_code {
74 bevy::prelude::KeyCode::ArrowLeft => KeyCode::Left,
75 bevy::prelude::KeyCode::ArrowRight => KeyCode::Right,
76 bevy::prelude::KeyCode::KeyQ => KeyCode::Char('q'),
77 _ => KeyCode::Null,
78 },
79 kind: match ev.state {
80 ButtonState::Pressed => KeyEventKind::Press,
81 ButtonState::Released => KeyEventKind::Release,
82 },
83 state: KeyEventState::NONE,
84 modifiers: KeyModifiers::NONE,
85 }));
86 }
87}
88
89#[derive(Component)]
90pub struct Cube;
91
92fn setup_scene_system(
93 mut commands: Commands,
94 mut meshes: ResMut<Assets<Mesh>>,
95 mut materials: ResMut<Assets<StandardMaterial>>,
96) {
97 commands.spawn((
98 Cube,
99 Mesh3d(meshes.add(Cuboid::default())),
100 MeshMaterial3d(materials.add(StandardMaterial {
101 base_color: Color::srgb(0.4, 0.54, 0.7),
102 ..default()
103 })),
104 ));
105 commands.spawn((
106 PointLight {
107 shadows_enabled: true,
108 ..Default::default()
109 },
110 Transform::from_xyz(3., 4., 6.),
111 ));
112}
113
114pub fn handle_input_system(
115 mut rat_events: EventReader<KeyEvent>,
116 mut cube: Query<&mut Transform, With<Cube>>,
117 mut exit: EventWriter<AppExit>,
118 time: Res<Time>,
119) {
120 for key_event in rat_events.read() {
121 match key_event.kind {
122 KeyEventKind::Press | KeyEventKind::Repeat => match key_event.code {
123 KeyCode::Char('q') => {
124 exit.send_default();
125 }
126 KeyCode::Left => {
127 cube.single_mut().rotate_z(-10. * time.delta_secs());
128 }
129 KeyCode::Right => {
130 cube.single_mut().rotate_z(10. * time.delta_secs());
131 }
132 _ => {}
133 },
134 _ => {}
135 }
136 }
137}