1use frug::{Color, Event, Instance, Keycode, Vec2d};
2
3struct GameObject {
4 pos: Vec2d<i32>,
5 size: Vec2d<u32>,
6 vel: Vec2d<i32>,
7 color: Color,
8 speed: i32,
9}
10
11impl GameObject {
12 fn get_center(&self) -> Vec2d<i32> {
13 Vec2d {
14 x: self.pos.x + (self.size.x / 2) as i32,
15 y: self.pos.y + (self.size.y / 2) as i32,
16 }
17 }
18
19 fn update(&mut self) {
20 self.pos += self.vel;
21 }
22
23 fn render(&self, instance: &mut Instance) {
24 instance.draw_rect(&self.pos, &self.size, self.color);
25 }
26
27 fn collide_w_object(&mut self, obj: &GameObject) {
28 if self.pos.y < obj.pos.y + obj.size.y as i32 && self.pos.y + self.size.y as i32 > obj.pos.y
30 {
31 if self.pos.x < obj.pos.x + obj.size.x as i32
33 && self.pos.x + self.size.x as i32 > obj.pos.x
34 {
35 self.vel.x *= -1;
36 }
37 }
38 }
39}
40
41impl Default for GameObject {
42 fn default() -> Self {
43 Self {
44 pos: Vec2d { x: 0, y: 0 },
45 size: Vec2d { x: 0, y: 0 },
46 vel: Vec2d { x: 0, y: 0 },
47 color: Color::RGB(255, 255, 255),
48 speed: 4,
49 }
50 }
51}
52
53fn check_reset_ball(ball: &mut GameObject, window_width: i32, window_height: i32) {
54 if ball.pos.x > window_width || (ball.pos.x + ball.size.x as i32) < 0 {
55 ball.pos.x = window_width / 2 - (ball.size.x / 2) as i32;
56 ball.vel.x *= -1;
57 ball.pos.y = window_height / 2 - (ball.size.y / 2) as i32;
58 }
59}
60
61fn main() {
62 const WINDOW_WIDTH: u32 = 800;
63 const WINDOW_HEIGHT: u32 = 600;
64 const TARGET_FPS: u32 = 60;
65 const FRAME_TIME: f32 = 1000.0 / TARGET_FPS as f32;
66
67 let mut frug_instance = Instance::new("Pong", WINDOW_WIDTH, WINDOW_HEIGHT);
68 let background_color = Color::RGB(0, 0, 0);
69
70 let mut player = GameObject {
72 pos: Vec2d {
73 x: 50,
74 y: (WINDOW_HEIGHT / 2 - 80 / 2) as i32,
75 },
76 size: Vec2d { x: 10, y: 80 },
77 ..Default::default()
78 };
79 let mut enemy = GameObject {
80 pos: Vec2d {
81 x: (WINDOW_WIDTH - 50 - 10) as i32,
82 y: player.pos.y,
83 },
84 size: player.size.clone(),
85 ..Default::default()
86 };
87 let mut ball = GameObject {
88 pos: Vec2d {
89 x: (WINDOW_WIDTH / 2 - 5) as i32,
90 y: player.pos.y,
91 },
92 size: Vec2d { x: 10, y: 10 },
93 ..Default::default()
94 };
95
96 ball.vel.x = ball.speed;
98 ball.vel.y = -ball.speed;
99
100 let mut player_up = 0;
102 let mut player_down = 0;
103
104 'running: loop {
105 let start_time = std::time::Instant::now();
107
108 for event in frug_instance.get_events() {
110 match event {
111 Event::Quit { .. }
113 | Event::KeyDown {
114 keycode: Some(Keycode::Escape),
115 ..
116 } => break 'running,
117 Event::KeyDown {
118 keycode: Some(Keycode::Up),
119 ..
120 } => {
121 player_up = 1;
122 }
123 Event::KeyUp {
124 keycode: Some(Keycode::Up),
125 ..
126 } => {
127 player_up = 0;
128 }
129 Event::KeyDown {
130 keycode: Some(Keycode::Down),
131 ..
132 } => {
133 player_down = 1;
134 }
135 Event::KeyUp {
136 keycode: Some(Keycode::Down),
137 ..
138 } => {
139 player_down = 0;
140 }
141 _ => {}
142 }
143 }
144
145 player.vel.y = (player_down - player_up) * player.speed;
147
148 let ball_center_y = ball.get_center().y;
150 let enemy_center_y = enemy.get_center().y;
151 if ball_center_y < enemy_center_y {
152 enemy.vel.y = -enemy.speed;
153 } else if ball_center_y > enemy_center_y {
154 enemy.vel.y = enemy.speed;
155 }
156
157 if ball.pos.y <= 0 || ball.pos.y + ball.size.y as i32 >= WINDOW_HEIGHT as i32 {
160 ball.vel.y *= -1;
161 }
162 ball.collide_w_object(&player);
164 ball.collide_w_object(&enemy);
165 player.update();
169 enemy.update();
170 ball.update();
171 check_reset_ball(&mut ball, WINDOW_WIDTH as i32, WINDOW_HEIGHT as i32);
172
173 frug_instance.clear(background_color);
175 player.render(&mut frug_instance);
176 enemy.render(&mut frug_instance);
177 ball.render(&mut frug_instance);
178 frug_instance.present();
179
180 let elapsed_time = start_time.elapsed();
182 let sleep_time = FRAME_TIME - elapsed_time.as_millis() as f32;
183 if sleep_time > 0.0 {
184 std::thread::sleep(std::time::Duration::from_millis(sleep_time as u64));
185 }
186 }
187}